简体   繁体   中英

How to SELECT rows with MIN(DateTime column), GROUP by another column and DISTINCT by another column in SQL?

My table is:

id student_id exam_date license result
1 101 01-11-2020 B2 FAILED
2 102 15-11-2020 A PASSED
3 103 22-11-2020 D FAILED
4 101 01-10-2020 D PASSED
5 104 01-12-2020 A PASSED
6 103 29-11-2020 D PASSED
7 101 01-12-2020 B2 PASSED
8 105 01-09-2020 B2 FAILED
9 104 01-11-2020 A FAILED
10 105 01-11-2020 B2 PASSED

I need to select the results that would have the first result according to the exam date according to each student id and the license column. If the same student takes different license exam, these two results need to come up as well. But I need only one result row for each student id and license value.

The result should look like this:

id student_id exam_date license result
1 101 01-11-2020 B2 FAILED
2 102 15-11-2020 A PASSED
3 103 22-11-2020 D FAILED
4 101 01-10-2020 D PASSED
8 105 01-09-2020 B2 FAILED
9 104 01-11-2020 A FAILED

I've done the research and queries and so far I only got 1 row for student_id although the student takes two different license examination.

The following is my query:

SELECT scct_outer.id, scct_outer.stud_id, scct_outer.exam_date, scct_outer.license, scct_outer.result  
    FROM stud_cdl_comp_test AS scct_outer
    INNER JOIN 
        (SELECT stud_id, MIN(exam_date) AS MinExamDate
        FROM stud_cdl_comp_test AS scct
        INNER JOIN stud AS s ON scct.stud_id = s.id 
        INNER JOIN agent_profile AS ap ON s.agent_profile_id = ap.id
        GROUP BY stud_id) groupedscct
    ON scct_outer.stud_id = groupedscct.stud_id
    AND scct_outer.exam_date = groupedscct.MinExamDate

Thinking that you are grouping by student_id in this case is almost incorrect in this case. What are actually grouping by is student + license. Let's call this key combination individual_license .

Here's what the solution will look like:

SELECT 
    st.id,
    st.stud_id,
    st.exam_date,
    st.license,
    st.result  
FROM stud_cdl_comp_test AS st
INNER JOIN
   (SELECT
         MIN(exam_date) AS min_date,
         st_inner.student_id,
         st_inner.license
    FROM stud_cdl_comp_test AS st_inner
    GROUP BY st_inner.student_id, st_inner.license
    ) grouped_inner
    ON grouped_inner.student_id = st.student_id
        AND grouped_inner.license = st.license
        AND grouped_inner.min_date = st.exam_date;

This should work.

The problem with you original code is that it is missing a correlartion on the licences between the outer query and the subquery. You would phrase it as:

select s.* 
from stud_cdl_comp_test s
inner join (
     select student_id, licence, min(exam_date) as minexamdate
     from stud_cdl_comp_test as scct
     group by stud_id, licence
) s1 on s1.student_id = s.student_id and s1.license = s.license and s1.minexamdate = s.date

I have no idea what stud and agent_profile are, so I removed the from the query.

That said, this is not the method I would recommend - a simple and efficient option is to filter with a subquery:

select *
from stud_cdl_comp_test s
where s.exam_date = (
    select min(s1.exam_date)
    from stud_cdl_comp_test s1
    where s1.student_id = s.student_id and s1.license = s.license
)

This can take advantage of an index on (student_id, license, exam_date) .

Alternatively, you can use row_number() , available in MySL 8.0:

select *
from (
    select s.*, 
        row_number() over(partition by student_id, licence order by exam_date) rn
    from stud_cdl_comp_test s
) s
where rn = 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM