使用CASE MySQL

时间:2016-06-04 12:41:21

标签: mysql count sql-order-by case

我有一个“积分榜”查询,显示我所在的联赛的统计数据。我试图根据一些条件订购查询。

  1. 如果团队的积分排名为1-25,则按胜利排序
  2. 如果球队排名前25位(积分排在第26位至第42位),请按积分排序。
  3. 我能描述它的最好方法是它应该像两张桌子一样。排名(1-25)的队伍应该排在一起,排名(26-42)的队伍应该排在一起。

    这是我的查询。

    SELECT m.Team AS team
    , SUM( r.points ) AS points
    , SUM(CASE
        WHEN rank = 1 THEN 1
        ELSE 0
      END) AS Wins
    , SUM(CASE
        WHEN rank < 6 THEN 1
        ELSE 0
      END) AS T5
    , SUM(CASE
        WHEN rank < 11 THEN 1
        ELSE 0
      END) AS T10
    , SUM(CASE
        WHEN rank < 21 THEN 1
        ELSE 0
      END) AS T20
    , ROUND(Avg(r.points),2) AS ppr
    , ROUND(Avg(r.rank),2) as avg_finish
    , MIN(r.rank) as best
    FROM members m
    LEFT JOIN results r ON r.team_id = m.M_ID
    GROUP BY team
    ORDER BY 
        CASE
            WHEN COUNT(*) < 26
            THEN Wins 
            WHEN COUNT(*) > 25
            THEN points
        END DESC, points DESC;
    

    由于我的别名“Wins”,我收到错误“引用'Wins'不支持(引用群组功能)。相反,我尝试过:

    ORDER BY 
        CASE
            WHEN COUNT(*) < 26
            THEN SUM(CASE
                WHEN rank = 1 THEN 1
                ELSE 0
            END)  
            WHEN COUNT(*) > 25
            THEN points
        END DESC, points DESC;
    

    这成功订购了我的桌子,但没有考虑COUNT(*)&gt; 25 CASE条款,只是按顺序,然后点。关于如何重构ORDER BY子句的任何想法?

5 个答案:

答案 0 :(得分:1)

您应该在外部查询中执行ORDER BY,因为Wins子句中无法访问列别名ORDER

SELECT * FROM (
SELECT m.Team AS team
, SUM( r.points ) AS points
, SUM(CASE
    WHEN rank = 1 THEN 1
    ELSE 0
  END) AS Wins
, SUM(CASE
    WHEN rank < 6 THEN 1
    ELSE 0
  END) AS T5
, SUM(CASE
    WHEN rank < 11 THEN 1
    ELSE 0
  END) AS T10
, SUM(CASE
    WHEN rank < 21 THEN 1
    ELSE 0
  END) AS T20
, ROUND(Avg(r.points),2) AS ppr
, ROUND(Avg(r.rank),2) as avg_finish
, MIN(r.rank) as best
FROM members m
LEFT JOIN results r ON r.team_id = m.M_ID
GROUP BY team ) XXX
ORDER BY 
    CASE
        WHEN COUNT(*) < 26
        THEN Wins 
        WHEN COUNT(*) > 25
        THEN points
    END DESC, points DESC;

答案 1 :(得分:0)

Wins的引用应该有效。 MySQL接受order by中的列别名,因此不需要子查询。

但是,您可能希望逻辑为:

ORDER BY (CASE WHEN COUNT(*) < 26 THEN 1 ELSE 2 END),  -- put the top ranking first,
         (CASE WHEN COUNT(*) < 26 THEN wins END) DESC,
         points DESC

在MySQL中,您还可以简化select逻辑:

SELECT m.Team AS team, SUM( r.points ) AS points,
       SUM(rank = 1) then Wins,
       SUM(rank < 6) as T5,
       SUM(rank < 11) as T10,
       SUM(rank < 21) as T20
       . . .

编辑:

我认为您想要的ORDER BY是:

ORDER BY (points <= 26) DESC,  -- put the top ranking first,
         wins desc,
         points DESC

答案 2 :(得分:0)

不幸的是,MySQL不支持ROW_NUMBER()RANK()等分析函数。如果确实如此,我们可以使用ROW_NUMBER()函数根据特定订单识别“前25行”。但是有两种方法可以模拟ROW_NUMBER()函数。

一种方法是使用带有用户定义变量的技巧来模拟它。我们使用这种方法是我们需要标记行,并返回值。

在这种情况下,我们只关心一支球队是否“进入前25名”。所以我们可以运行一个单独的查询来返回“前25名”团队。

例如,此查询获取两个集......“所有团队”(a)的集合结果以及“前25名”团队的集合({{ 1}}),并匹配使用外连接操作的那些:

t

内联视图“a”是获取所有团队的查询。

内联视图“t”本质上是相同的查询,但有一个ORDER BY和LIMIT子句,因此它最多返回25行。 ORDER BY在LIMIT之前应用,因此它将返回具有最高分数的25支球队。 (其他表达式包含在ORDER BY中,以使结果更具确定性......如果列表中第25和第26的两个团队具有相同的点数......

外部查询识别来自 SELECT a.* FROM ( SELECT m.team AS team , SUM( r.points ) AS points , SUM(IF( r.rank=1 ,1,0)) AS wins , SUM(IF( r.rank<6 ,1,0)) AS t5 , SUM(IF( r.rank<11 ,1,0)) AS t10 , SUM(IF( r.rank<21 ,1,0)) AS t20 , ROUND(AVG( r.points ),2) AS ppr , ROUND(AVG( r.rank ),2) AS avg_finish , MIN( r.rank ) AS best FROM members m LEFT JOIN results r ON r.team_id = m.m_id GROUP BY m.team ) a LEFT JOIN ( SELECT tm.team AS team , SUM(IF( tr.rank=1 ,1,0)) AS wins FROM members tm LEFT JOIN results tr ON tr.team_id = tm.m_id GROUP BY tm.team ORDER BY SUM( tr.points ) DESC , SUM(IF( tr.rank=1 ,1,0)) DESC , tr.team_id DESC LIMIT 25 ) t ON t.team = ta.team ORDER BY t.wins DESC , a.points DESC , a.team DESC 的哪个团队位于“前25名”中,并应用适当的排序。 (请注意,没有来自a的匹配行的团队将为t中的所有列提供NULL值...即t将为NULL,因此ORDER BY可以正常工作。

我们不一定需要从内嵌视图t.wins返回wins。我们可以通过测试NULL值来检查是否从t返回了匹配的行。例如

t

答案 3 :(得分:0)

您的问题是,在按points对结果进行排序之前,您不知道排名。如果你知道等级,你可以用

排序
ORDER BY (rank > 25) ASC
, CASE WHEN rank <= 25 THEN Wins ELSE 0 END
, points

确定MySQL排名的一种方法是将排序结果存储在具有AUTO_INCREMENT排名列的临时表中。

DROP TEMPORARY TABLE IF EXISTS tmp_teams;
CREATE TEMPORARY TABLE tmp_teams (
    rank INT AUTO_INCREMENT PRIMARY KEY,
    team varchar(255),
    points INT,
    Wins INT,
    T5 INT,
    T10 INT,
    T20 INT,
    ppr INT,
    avg_finish INT,
    best INT
) AS
    SELECT null AS rank
    , m.Team AS team
    , SUM( r.points ) AS points
    , SUM(CASE
        WHEN rank = 1 THEN 1
        ELSE 0
      END) AS Wins
    , SUM(CASE
        WHEN rank < 6 THEN 1
        ELSE 0
      END) AS T5
    , SUM(CASE
        WHEN rank < 11 THEN 1
        ELSE 0
      END) AS T10
    , SUM(CASE
        WHEN rank < 21 THEN 1
        ELSE 0
      END) AS T20
    , ROUND(Avg(r.points),2) AS ppr
    , ROUND(Avg(r.rank),2) as avg_finish
    , MIN(r.rank) as best
    FROM members m
    LEFT JOIN results r ON r.team_id = m.M_ID
    GROUP BY team
    ORDER BY points DESC, Wins DESC, r.team_id ASC -- define the order for rank
;

SELECT *
FROM tmp_teams
ORDER BY (rank > 25) ASC
, CASE WHEN rank <= 25 THEN Wins ELSE 0 END
, points;

另一种方法是使用会话变量(@rank),它将在有序子查询中递增:

SELECT *
FROM (
    SELECT m.Team AS team
    , SUM( r.points ) AS points
    , SUM(CASE
        WHEN rank = 1 THEN 1
        ELSE 0
      END) AS Wins
    , SUM(CASE
        WHEN rank < 6 THEN 1
        ELSE 0
      END) AS T5
    , SUM(CASE
        WHEN rank < 11 THEN 1
        ELSE 0
      END) AS T10
    , SUM(CASE
        WHEN rank < 21 THEN 1
        ELSE 0
      END) AS T20
    , ROUND(Avg(r.points),2) AS ppr
    , ROUND(Avg(r.rank),2) as avg_finish
    , MIN(r.rank) as best
    , @rank := @rank + 1 AS rank
    FROM members m
    LEFT JOIN results r ON r.team_id = m.M_ID
    CROSS JOIN (select @rank := 0) AS init_rank
    GROUP BY team
    ORDER BY points DESC, Wins DESC, r.team_id ASC -- define the order for rank
    LIMIT 1000000000 -- workaround for some versions/settings to force sorting a subquery result
) sub
ORDER BY (rank > 25) ASC
, CASE WHEN rank <= 25 THEN Wins ELSE 0 END
, points

答案 4 :(得分:0)

它终于奏效了。保罗,你的排名是正确的。以下是Mahmoud提供我使用的排名的链接: Rank Values

SET @rownum = 0; 

SELECT *, (@rownum := @rownum + 1) AS chase FROM (
SELECT m.Team AS team
, SUM( r.points ) AS points
, SUM(r.rank = 1) AS Wins
, SUM(r.rank < 6) AS T5
, SUM(r.rank < 11) AS T10
, SUM(r.rank < 21) AS T20
, ROUND(Avg(r.points),2) AS ppr
, ROUND(Avg(r.rank),2) as avg_finish
, MIN(r.rank) as best
FROM members m
LEFT JOIN results r ON r.team_id = m.M_ID
GROUP BY team
ORDER BY points DESC ) AS ptotal
ORDER BY CASE
    WHEN chase <= 24
    THEN Wins
END DESC, points DESC;

谢谢大家的帮助。我刚刚开始使用这个网站,无法相信你们都有多大帮助。当我在路上遇到问题时,我知道在哪里看看!