简体   繁体   中英

mysql select top N records conditionally

i have the following table :

+------+------+-----+---------+------------+
| no   | code |eot  |group_id | compulsary |
+------+------+-----+---------+------------+
| 1005 | 101  |  51 |     1   |          1 | 
| 1005 | 102  |  67 |     1   |          1 | 
| 1005 | 121  |  65 |     1   |          1 |
| 1005 | 231  |  82 |     2   |          0 |
| 1005 | 232  |  56 |     2   |          0 |
| 1005 | 233  |  45 |     2   |          1 |
| 1005 | 313  |  80 |     3   |          0 |
| 1005 | 443  |  50 |     4   |          0 |     

|------+------+-----+---------+------------+

now what i want is :

1.) return all records where group_id=1 ,

2.) return the best two from group_id=2(however if compulsary=1 then include that row and the best from the remaining group_id=2 ),

3.)return 1 row each from group_id=3 and group_id=4 and if compulsary=1 then return that row

the final result should have only seven rows:

+------+------+-----+---------+------------+
| no   | code |eot  |group_id | compulsary |
+------+------+-----+---------+------------+
| 1005 | 101  |  51 |     1   |          1 | 
| 1005 | 102  |  67 |     1   |          1 | 
| 1005 | 121  |  65 |     1   |          1 |
| 1005 | 231  |  82 |     2   |          0 |
| 1005 | 233  |  45 |     2   |          1 |
| 1005 | 313  |  80 |     3   |          0 |
| 1005 | 443  |  50 |     4   |          0 |     

|------+------+-----+---------+------------+

with the compulsary=1 rows inluded like above;

so far I have this query though I don't know how to check for the compulsary to get what I want:

select rg.* 
from 
(
  select * 
  from 
  (
    select 
      rgrade.*,
      @rn := if(@gr=group_id,if(@gr:=group_id,@rn+1,@rn+1),if(@gr:=group_id,1,1)) as rn 
    from rgrade 
    cross join (select @rn:=0,@gr:=0) as vars 
    where admission_no=1005
  ) v 
  where (group_id=1) 
     or (group_id=2 and if(compulsary=1,rn<=1,rn<=2)) 
     or (group_id in (3,4) and rn=1)
) rg  
order by group_id;

the query returns the seven rows as expected but does not check for compulsary in group_id=2 .

Any help much appreciated

You are describing several queries here, the result of which you want combined: All group_id = 1 , the two best group_id = 2 , the best of group_id = 3 , and the best of group_id = 4 . So write these queries and combine them with UNION ALL . With "best" defined as compulsary = 1 preferred, then highest eot, you get:

(select * from mytable where group_id = 1)
union all
(select * from mytable where group_id = 2 order by compulsary = 1 desc, eot desc limit 2)
union all
(select * from mytable where group_id = 3 order by compulsary = 1 desc, eot desc limit 1)
union all
(select * from mytable where group_id = 4 order by compulsary = 1 desc, eot desc limit 1)
order by group_id, no, code
;

You are trying to mimic standard SQL's

row_number() over (partition by group_id 
                   order by case when compulsary = 1 then 1 else 0 end desc, eot desc)

with MySQL means. And I see the partitioning by group_id in your query, but I don't see any ORDER BY to get the best records first.

Here is my attempt on it. There may be mistakes; I'm no MySQL guy.

select
  no, code, eot, group_id, compulsary
from
(
  select 
    no, 
    code,
    eot,
    compulsary,
    @row_number := 
      case when group_id = @last_group_id then @row_number + 1 else 1 end as row_number,
    @last_group_id := group_id as group_id
  from rgrade
  cross join 
  (
    select 
      @row_number := 0, 
      @last_group_id := -1
  ) as vars
  where admission_no = 1005
  order by group_id, (compulsary = 1) desc, eot desc
) ranked
where (group_id = 1)
   or (group_id = 2 and row_number <= 2)
   or (group_id = 3 and row_number = 1)
   or (group_id = 4 and row_number = 1)
order by group_id, code;

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