从先前记录中选择并使用两个主要ORDER BY

时间:2011-10-28 14:34:35

标签: mysql sql greatest-n-per-group

用户:

user_id    user_name     
---------------------

 1          User A
 2          User B

跟踪:

user_id     track         
---------------------

 1           no
 2           no

应用:

user_id    date_of_application   date_ended    grade    status    
---------------------------------------------------------------

 1            2011-01-01         2011-02-28     1.0     Ended
 1            2011-02-02         2011-03-28     1.0     Ended
 1            2011-03-03         2011-04-28     (1.5)   Ended

 2            2011-01-01         2011-02-20     2.0     Ended
 2            2011-02-02         2011-03-11     2.5     Ended
 2            2011-03-03         2011-04-28     (1.0)   Ended

 1            2011-05-10              -          -      Pending
 2            2011-05-15              -          -      Pending
  • 请注意,只要所有以前的应用程序已结束(状态=已结束),该表就可以包含同一用户的多条记录
  • user_id 不是唯一的(仅适用于应用程序表)
  • 日期为 yy-mm-dd 格式
  • date_ended 成绩仅在应用程序结束时更新
  • 另外,我知道可能建议'status'拥有自己的表,但是我希望上面的表格按原样排除(当然减去错别字和重大错误)

我想在这里完成的是检索所有行 WHERE状态为'待定',并且每个检索到的行的成绩列的值为最新成绩的值(换句话说,带有最新日期_ 的行),(在上面的括号中),此特定用户(或行)的状态为“已结束” )。

另外,我需要将结果的前10行设置为等级ASC 。然后将后续行(第11行直到最后一行)排序为 date_of_application ASC

显然,SQL查询不是我最强的区域,因此我不确定使用2个或更多查询执行这些ORDER BY是否更好(或仅可能)。但是我更喜欢使用单个查询来完成此操作。

期望的结果:

user_id   user_name   date_of_application   grade   status   track 
--------------------------------------------------------------------

 1         User A         2011-05-10        (1.5)    Pending    no
 2         User B         2011-05-15        (1.0)    Pending    no

我到目前为止工作代码[减去可能的拼写错误],(并列出了要应用的附加内容)

  • 最新成绩
  • ORDER BY成绩(前10名),ORDER BY date_of_application(第11名至最后一行)

查询:

SELECT users.user_name,
       t.track,
       a.user_id,
       a.date_of_application,
       a.status,
       (SELECT ae.grade
          FROM applications AS ae
          WHERE ae.status = 'Ended' 
            AND ae.user_id = a.user_id                                                            
         LIMIT 1) AS grade
  FROM users
  JOIN applications AS a ON users.user_id = a.user_id
  JOIN tracking AS t ON users.user_id = t.user_id
 WHERE a.status = 'Pending'
ORDER BY grade ASC

2 个答案:

答案 0 :(得分:2)

你可能在这里尝试在一个查询中做太多。

无论如何,如果你想要伤害你的眼睛:

 select a.* from
 (
 SELECT u.user_name,
 a.user_id,
 a.date_of_application,
 td.grade,
 a.status,
 t.track
 FROM users u
 JOIN applications AS a ON u.user_id = a.user_id
 JOIN tracking AS t ON u.user_id = t.user_id
 LEFT OUTER JOIN
 (
 select ap.user_id,ap.grade 
 from applications ap
 inner join
 (select a.user_id,max(date_ended) as max_ended_date
 from applications a
 where a.status = 'Ended'
 group by a.user_id
 ) md on md.user_id = ap.user_id and ap.date_ended = md.max_ended_date
 ) as td on u.user_id = td.user_id
 WHERE a.status = 'Pending'
 ORDER BY cast(replace(replace(td.grade,'(',''),')','') as decimal(12,2)),u.user_id ASC
 LIMIT 10
 ) a
 WHERE grade is not null
 UNION ALL
 select b.* from
 (
 SELECT u.user_name,
   u.user_id,
   a2.date_of_application,
   td.grade,
   ifnull(a2.status,'No applications yet') as status,
   t2.track
 FROM users u
 LEFT OUTER JOIN (select user_id,date_of_application,status from applications where     status = 'Pending') AS a2 ON u.user_id = a2.user_id
 JOIN tracking AS t2 ON u.user_id = t2.user_id
 LEFT OUTER JOIN
 ( 
 select ap.user_id,ap.grade 
 from applications ap
 inner join
  (select a.user_id,max(date_ended) as max_ended_date
  from applications a
  where a.status = 'Ended'
  group by a.user_id
 ) md on md.user_id = ap.user_id and ap.date_ended = md.max_ended_date
 ) as td on u.user_id = td.user_id
 where u.user_id not in (
 select t1.user_id
 from (
 select ap1.user_id,ap1.grade 
  from applications ap1
  inner join
  (select a1.user_id,max(date_ended) as max_ended_date
   from applications a1
   where a1.status = 'Ended'
   group by a1.user_id
  ) md1 on md1.user_id = ap1.user_id and ap1.date_ended = md1.max_ended_date
  order by cast(replace(replace(ap1.grade,'(',''),')','') as decimal(12,2)),md1.user_id asc
  limit 10
  ) as t1
 )
 ORDER BY status desc,a2.date_of_application ASC
 ) b;

这确实做出以下假设:

  1. userstracking中的每个user_id始终只有一行 a
  2. 修改

    稍微解释一下这个问题:

    内联视图别名ORDER BY cast(replace(replace(td.grade,'(',''),')','') as decimal(12,2)),u.user_id ASC (又名“上半部分”)根据最近的“已结束”等级提升,返回前10名用户的列表。请注意查询的以下部分从成绩中删除任何括号,将结果数字转换为小数位数到2位小数,并按等级递增,然后,如果等级成绩相同,则按user_id排序:

    b

    内联视图a与内嵌视图DESC几乎相同,不同之处在于排除了出现在上半部分的用户,并按状态ASC对结果进行排序(以移动这些用户)没有应用程序到列表底部)和申请日期{{1}}。

答案 1 :(得分:1)

这应该对你有用...为了弄清楚发生了什么,你必须从查询的最内部开始。对于每个用户,找到最高的“待定”日期(因为如您所述,只有一个),以及最后一个“结束”类日期。按用户分组。这将保证每个用户一条记录,预先计算为PreQuery。

接下来,按用户和上次结束日期自行联接回应用程序表TWICE ...一次,然后按用户和上次挂起日期进行。通过LEFT JOIN,如果你只有一个人有一个应用程序但没有结束,他们将被包括在内......同样,如果只有一个完整的类没有更多的待处理申请,他们也将被包括在内。

从这些别名参考中拉出相应的列以获得成绩。虽然我们正在使用SQL变量,但使用此查询的顺序,DESCENDING将从1-n中获得最佳成绩而不考虑申请日期。

最后,取整个结果集并按照特殊顺序排序......按条件排序,如果用户的等级小于11,则使用其顺序。否则,让其他所有人按顺序为第一个订单分配相同的“11”值...之后,按申请日期排序。

小块依赖于前一组。这个不应该让你的头受伤,也不需要任何工会

select
      QryRank.*
   from
      ( select
            PreQuery.User_ID,
            usr.user_Name,
            trk.Track,
            PreQuery.LastEnded,
            appEnd.Grade,
            PreQuery.LastPend as Date_Of_Application,
            @Rank := @Rank +1 UserRank
         from
            ( select
                  app.user_id,
                  max( if( app.status = "Ended", date_ended, null ) ) as LastEnded,
                  max( if( app.status = "Pending", app.date_of_application, null )) LastPend
               from
                  Applications app
               group by
                  app.user_id ) PreQuery

            LEFT JOIN Applications appEnd
               on PreQuery.User_ID = appEnd.User_ID
              AND PreQuery.LastEnded = appEnd.date_ended

            LEFT JOIN Applications appPend
               on PreQuery.User_ID = appPend.User_ID
               AND PreQuery.LastPend = appPend.date_of_application

            join Users usr   
               on PreQuery.user_id = usr.user_id

            join Tracking trk
               on PreQuery.user_id = trk.user_id,

            ( select @Rank := 0 ) sqlvars

         order by
            appEnd.Grade DESC ) QryRank
    order by
       if( QryRank.UserRank < 11, QryRank.UserRank, 11 ),
       QryRank.Date_Of_Application