Selecting heighest number in sql with the same id - mysql

I am trying to select max transaction_num from my table tbl_loan and group it by c_id to avoid duplicate of c_id.
here is my query
SELECT * FROM `tbl_loan` WHERE transaction_num IN (SELECT max(transaction_num) max_trans FROM tbl_loan GROUP BY c_id)
and my output is
still have duplicate c_id.

MySQL MAX with GROUP BY clause
To find the maximum value for every group, you use the MAX function with the GROUP BY clause in a SELECT statement.
You use the following query:
SELECT
*, MAX(transaction_num)
FROM
tbl_loan
GROUP BY c_id
ORDER BY MAX(transaction_num);

From the looks of it, and correct me if I'm wrong. The transaction number appears to be sequential per each C_ID whenever a new transaction happens. There is also the "I_ID" column which appears to be an auto-incrementing column which does not duplicate. It appears your transaction number is sequentially 1, 2, 3, etc per C_ID for simple counting purposes, so everyone starts with a 1, and those with more have 2nd and possibly 3rd and more...
So, if this is accurate and you want the most recent per C_ID, you really want the max "I_ID" per C_ID because multiple records will exist with a value of 2, 3, etc...
try this.
SELECT
TL.*
FROM
tbl_loan TL
JOIN ( SELECT C_ID, max(I_ID) maxI_ID
FROM tbl_loan
GROUP BY c_id) MaxPer
on TL.I_ID = MaxPer.MaxI_ID
So, from your data for C_ID = 55, you have I_ID = 61 (trans num = 1) and 62 (trans num = 2). So for ID = 55, you want the transaction I_ID = 62 which represents the second transaction.
For C_ID = 70, it has I_IDs of 77 & 78, of which will grab I_ID = 78.
The rest only have a single trans num and will get their only single entry id.
HTH

Think about it like this
Your query:
SELECT * FROM `tbl_loan` WHERE transaction_num IN (SELECT max(transaction_num) max_trans FROM tbl_loan GROUP BY c_id)
Lets say your subquery returns one transaction_num of 20. This 20 can be the same for multiple c_id's.
So your outer query is then running
SELECT * FROM `tbl_loan` WHERE transaction_num IN (20)
and returning all those results.

Related

Filter duplicate records and count of occurance corrosponding to a filter condition in SQL

There is a table and group the records using key: stu_class|stu_birth|stu_major. If there are duplicate records, the record with the smallest stu_id is selected. So, I need to count the total number of records with satisfied this condition.
Example:
Here, stu_id (100,101) are duplicate records based on the key. But I want to select only the smallest stu_id record. It is stu_id , 100. Simillary, stu_id (102,104) are duplicate records. but need to select stu_id 102.
Then selected record count should be 2. How can I get this count using SQL?. I mean how I can get calculated total number of records as 2.
One method uses window functions:
select t.*
from (select t.*,
row_number() over (partition by stu_class, stu_birth, stu_major order by stu_id) as seqnum
from t
) t
where seqnum = 1;
This is available in MySQL starting with version 8.
An alternative uses a correlated subquery and might be faster, even in version 8:
select t.*
from t
where t.stu_id = (select min(t2.stu_id)
from t t2
where t2.stu_class = t.stu_class and t2.stu_birth = t.stu_birth and t2.stu_major = t.stu_major
);
This can take advantage of an index on (stu_class, stu_birth, stu_major, stu_id).
EDIT
If you just want the total records, then use aggregation:
select stu_class, stu_birth, stu_major, min(stu_id), count(*) as cnt
from t
group by stu_class, stu_birth, stu_major;

Sql query max, group by

I am trying to get all students group by class_id, student_id, teacher_id
SO what I mean is this one :
Select id,class_id, student_id,teacher_id, max(active)
FROM student_classes
GROUP BY class_id, student_id, teacher_id
But this is what I get
Actually what I want as a result is:
114 137 1 47 1
108 138 2 49 0
113 197 3 47 1
So basically the problem is at the third row. Instead of having id = 113 I get ID=111.
What should I do in this case? Can you please help me with the query
As mentioned in the comments, MySQL allows something against the SQL standard, letting you include a non-aggregated column (in this case id) in the select list of a query that includes a group by. As far as I know, it will arbitrarily pick one row in each grouping and display the id value from that row.
If you have a specific rule about which id value you want to see, you need to express that in your query.
By the way, your desired output appears to have multiple typos (e.g. 197, which doesn't appear in your data at all).
From your comment (which you should edit into your original question), and your desired output, I think the rule you want for the id column is:
If there are any rows with active=1 in the group, choose the maximum id value from those rows
If all rows in the group have active=0, choose the minimum id value. (You didn't say this specifically; I'm assuming it based on the presence of 108 on the second row of your desired output.)
I think that this query will produce those results. (And also eliminate the non-standard MySQL behavior.)
SELECT
COALESCE(
MAX(CASE WHEN active=1 THEN id ELSE NULL END),
MIN(id)
) AS some_id
class_id, student_id, teacher_id, max(active)
FROM student_classes
GROUP BY class_id, student_id, teacher_id
MySQL versions 5.5, 5.6 works as you coded. But actually it's not correct. With version 5.7 and higher it will throw error. The error will be like "SELECT list is not in GROUP BY clause and contains nonaggregated column 'student_classes.id'..."
Therefore it seems your DB version is old and maybe this code should work as you wanted
select
---------
min(x.id) as id,
---------
x.class_id,
x.student_id,
x.active
from student_classes x
inner join (select
class_id,
student_id,
teacher_id,
---------
max(active) max_active
---------
from student_classes x
group by class_id, student_id, teacher_id
) y
on x.class_id = y.class_id and
x.student_id = y.student_id and
x.teacher_id = y.teacher_id and
x.active = y.max_active
group by x.class_id, x.student_id, x.active
order by id, class_id, student_id
;
You don't want an aggregation actually, but rather pick particular rows. The rule for picking a row is: Per class_id, student_id, teacher_id get the one with the maximum active and in case of a tie the lowest id. This is a ranking of rows.
As of MySQL 8 you can use a window function like ROW_NUMBER to rank rows:
select *
from
(
select
sc.*,
row_number() over (partition by class_id, student_id, teacher_id
order by active desc, id) as rn
from student_classes sc
) with_wanted_id
where rn = 1;
In older versions you could use NOT EXISTS to exclude rows for which a better row exists:
select *
from student_classes sc1
where not exists
(
select null
from student_classes sc2
where sc2.class_id = sc1.class_id
and sc2.student_id = sc1.student_id
and sc2.teacher_id = sc1.teacher_id
and
(
sc2.active > sc1.active
or
(sc2.active = sc1.active and sc2.id < sc1.id)
)
);

Number of different user's IDs occured in the table more than 3 times

I have a task to do, there is a table with different user IDs and SQL query should count the number of different users ids that occured in the table more than 3 times. For example IDs number that occured in the table is 1, 1, 2, 1, 3, 4, 3, 3 ,1, 2, 3, 4, 5, 4, 5, 4 since number 1, 3 and 4 occured in the table more than 3 time SQL query should return just one row number 3.
What i have done so far is:
SELECT COUNT(*) counter
FROM (SELECT COUNT(id) as counter_id FROM user_id GROUP BY id HAVING COUNT(id) >3)
I am not sure is that one correct if it is incorrect what will be correct one.
Have you tried to run your query?
SELECT COUNT(*) counter
FROM (SELECT COUNT(id) as counter_id
FROM user_id
GROUP BY id
HAVING COUNT(id) > 3
)
If so, you would get an error, because the subquery in the FROM clause requires an alias. In addition, it is strange that a table would be called user_id.
SELECT COUNT(*) counter
FROM (SELECT COUNT(id) as counter_id
FROM user_id
GROUP BY id
HAVING COUNT(id) > 3
) u
-------^
use distinct inside count() and take only id from subquery output
select count(distinct id) from ( SELECT id
FROM user GROUP BY id
HAVING COUNT(id) >3) a

mysql COUNT results with GROUP BY

I have a table with with 2 unique linked table ids.
I get the results I want with GROUP BY but when I count I only get the number of each group.
When I do:
SELECT COUNT(id) FROM my_table GROUP BY first_linked_table_id, second_linked_table_id
I get as results 1, 2, 3, 1 but I want 4 as a result.
I tried DISTINCT but I think that only works with one column
Your requirement is to get count of number of groups. So we need two operations-
Group(inner query)
Count(outer query)
Following query will do precisely that:
SELECT COUNT(*)
FROM
(
SELECT COUNT(id)
FROM my_table
GROUP BY first_linked_table_id,
second_linked_table_id
) t
If you want to count the rows, I think you're going to need a subquery. Something like this:
SELECT COUNT(*) FROM (
SELECT COUNT(id) FROM my_table GROUP BY first_linked_table_id, second_linked_table_id
);

include only the first and last groups in query results

Given the schema
The following query
SELECT a.user_id,
a.id,
a.date_created,
avg(ai.level) level
FROM assessment a
JOIN assessment_item ai ON a.id = ai.assessment_id
GROUP BY a.user_id, a.id;
Returns these results
user_id, a.id, a.date_created, level
1, 99, "2015-07-13 18:26:00", 4.0000
1, 98, "2015-07-13 19:04:58", 6.0000
13, 9, "2015-07-13 18:26:00", 2.0000
13, 11, "2015-07-13 19:04:58", 3.0000
I would like to change the query such that only the earliest results is returned for each user. In other words, the following should be returned instead
user_id, a.id, a.date_created, level
1, 99, "2015-07-13 18:26:00", 4.0000
13, 9, "2015-07-13 18:26:00", 2.0000
I think I need to add a HAVING clause, but I'm struggling to figure out the exact syntax.
I have done something like this, except for a small difference I wanted first 5 per group. The usage case was for reporting - means time for running query / creation of temp table was not a constraint.
The solution I had:
Create a new table with columns as id( a reference to original table) and id can be unique/primary
INSERT IGNORE INTO tbl1 (id) select min(id) from original_tbl where id not in (select id from tbl1) group by user_id
Repeat step 2 as many times you required( in my case it was 5 times). the new table table will have only the ids you want to show
Now run a join on tbl1 and original table will give you the required result
Note: This might not be the best solution, but this worked for me when I had to share the report in 2-3hours in a weekend. And the data size I had was around 1M records
Disclaimer: I am in a bit of a hurry, and have not tested this fully
-- Create a CTE that holds the first and last date for each user_id.
with first_and_last as (
-- Get the first date (min) for each user_id
select a.[user_id], min(a.date_created) as date_created
from assessment as a
group by a.[user_id]
-- Combine the first and last, so each user_id should have two entries, even if they are the same one.
union all
-- Get the last date (max) for each user_id
select a.[user_id], max(a.date_created)
from assessment as a
group by a.[user_id]
)
select a.[user_id],
a.id,
a.date_created,
avg(ai.[level]) as [level]
from assessment as a
inner join assessment_item as ai on a.id = ai.assessment_id
-- Join with the CTE to only keep records that have either the min or max date_created for each user_id.
inner join first_and_last as fnl on a.[user_id] = fnl.[user_id] and a.date_created = fnl.date_created
group by a.[user_id], a.id, a.date_created;