MYSQL操作顺序失败:ORDER BY正在影响SELECT语句的结果

时间:2016-07-28 21:54:54

标签: mysql sql

我正在尝试使用MySQL在HackerRank上解决这个问题。 https://www.hackerrank.com/challenges/occupations

  

旋转OCCUPATIONS中的Occupation列,以便每个Name按字母顺序排序并显示在其相应的Occupation下面。输出列标题应分别为Doctor,Professor,Singer和Actor。

#Input Format

Samantha Doctor
Jenny Doctor
Ashley Professor
...

#Sample Output

Jenny    Ashley     Meera  Jane
Samantha Christeen  Priya  Julia
NULL     Ketty      NULL   Maria

这是MYSQL的解决方案:

set @r1=0, @r2=0, @r3=0, @r4=0;
select min(Doctor), min(Professor), min(Singer), min(Actor) from(
    select case when Occupation='Doctor' then (@r1:=@r1+1)
        when Occupation='Professor' then (@r2:=@r2+1)
        when Occupation='Singer' then (@r3:=@r3+1)
        when Occupation='Actor' then (@r4:=@r4+1) end as RowNumber,
    case when Occupation='Doctor' then Name end as Doctor,
    case when Occupation='Professor' then Name end as Professor,
    case when Occupation='Singer' then Name end as Singer,
    case when Occupation='Actor' then Name end as Actor
from OCCUPATIONS
order by Name
) Temp
group by RowNumber

这会按字母顺序返回输出而不是随机顺序,因为RowNumber由于ORDER BY语句而发生了变化。

我的问题是为什么 order By Name 会影响RowNumber列的排序?

SQL中的操作顺序表示所有SELECT语句都发生在ORDER BY语句之前。当ORDER BY发生时,是否已经计算了行号?

修改
由于人们询问操作顺序,我已经包含了我所见过的多个地方的链接(更不用说我的SQL教科书)了: http://sqlbolt.com/lesson/select_queries_order_of_execution
http://www.bennadel.com/blog/70-sql-query-order-of-operations.htm

2 个答案:

答案 0 :(得分:1)

首先,其中是否按照SELECT之前ORDER BY 执行的操作顺序表示?这种说法根本不是真的。

是什么,SELECTORDER BYSELECT之前由编译器评估的。因此,ORDER BY中定义的列别名可用于SELECT

除此之外,变量是SQL的扩展。 MySQL非常明确SELECT是执行的最后一个子句:

  

ORDER BY语句中,每个select表达式仅在以下时进行计算   发送给客户。这意味着在HAVING,GROUP BY或ORDER BY中   子句,指的是在select中赋值的变量   表达式列表不能按预期工作。 。

因为在将结果返回给客户端之前发生getExternalFilesDirs(),所以在使用变量评估表达式之前对数据进行排序。

答案 1 :(得分:1)

评估顺序为 undefined

来自MySQL参考手册http://dev.mysql.com/doc/refman/5.7/en/user-variables.html

  

作为一般规则,除了在SET语句中,您不应该为用户变量赋值并在同一语句中读取值。例如,要增加变量,这没关系:

     

SET @a = @a + 1;

     

对于其他语句,例如SELECT,您可能会得到您期望的结果,但这不能保证。在下面的语句中,您可能会认为MySQL将首先评估@a,然后再进行一次分配:

     

SELECT @a, @a:=@a+1, ...;

     

但是,涉及用户变量的表达式的评估顺序未定义。

正如@Uueerdo评论的那样,我们通常可以通过内联视图(派生表)解决这个问题。在内联视图中执行ORDER BY,然后从中进行SELECT,并使用用户定义的变量执行操作。

另外,我不相信可以保证CASE表达式中的表达式不会被执行...... CASE表达式只需要返回表达式的值,它不会&#39 ; t保证作业不会发生。

有了类似的东西,行为仍未定义,但我们通常会观察到更一致的行为:

 SELECT MIN(IF(s.Occupation='Doctor'    ,s.Name,NULL)) AS Doctor
      , MIN(IF(s.Occupation='Professor' ,s.Name,NULL)) AS Professor
      , MIN(IF(s.Occupation='Singer'    ,s.Name,NULL)) AS Singer
      , MIN(IF(s.Occupation='Actor'     ,s.Name,NULL)) AS Actor
   FROM ( SELECT t.Name
               , t.Occupation
               , @r1 := @r1 + IF(t.Occupation='Doctor'    ,1,0) AS r1
               , @r2 := @r2 + IF(t.Occupation='Professor' ,1,0) AS r2
               , @r3 := @r3 + IF(t.Occupation='Singer'    ,1,0) AS r3
               , @r3 := @r4 + IF(t.Occupation='Actor'     ,1,0) AS r4
            FROM OCCUPATIONS t
           CROSS
            JOIN ( SELECT @r1:=0, @r2:=0, @r3:=0, @r4:=0 ) i
           WHERE t.Occupation IN ('Doctor','Professor','Singer','Actor')
           ORDER BY t.Name, t.Occupation
        ) s
  GROUP BY CASE s.Occupation
           WHEN 'Doctor'    THEN s.r1
           WHEN 'Professor' THEN s.r2
           WHEN 'Singer'    THEN s.r3
           WHEN 'Actor'     THEN s.r4
           ELSE NULL
           END
  ORDER BY CASE s.Occupation
           WHEN 'Doctor'    THEN s.r1
           WHEN 'Professor' THEN s.r2
           WHEN 'Singer'    THEN s.r3
           WHEN 'Actor'     THEN s.r4
           ELSE NULL
           END

(未经测试。)

由于操作顺序,这可能仍有问题。为了解决这个问题,我们可以使用内联视图让MySQL在执行行编号之前执行排序操作...

 SELECT MIN(IF(s.Occupation='Doctor'    ,s.Name,NULL)) AS Doctor
      , MIN(IF(s.Occupation='Professor' ,s.Name,NULL)) AS Professor
      , MIN(IF(s.Occupation='Singer'    ,s.Name,NULL)) AS Singer
      , MIN(IF(s.Occupation='Actor'     ,s.Name,NULL)) AS Actor
   FROM ( SELECT t.Name
               , t.Occupation
               , @r1 := @r1 + IF(t.Occupation='Doctor'    ,1,0) AS r1
               , @r2 := @r2 + IF(t.Occupation='Professor' ,1,0) AS r2
               , @r3 := @r3 + IF(t.Occupation='Singer'    ,1,0) AS r3
               , @r3 := @r4 + IF(t.Occupation='Actor'     ,1,0) AS r4
            FROM ( SELECT u.Name
                        , u.Occupation
                     FROM OCCUPATIONS u
                    WHERE u.Occupation IN ('Doctor','Professor','Singer','Actor')
                    ORDER BY u.Name
                 ) t
           CROSS
            JOIN ( SELECT @r1:=0, @r2:=0, @r3:=0, @r4:=0 ) i
        ) s
  GROUP BY CASE s.Occupation
           WHEN 'Doctor'    THEN s.r1
           WHEN 'Professor' THEN s.r2
           WHEN 'Singer'    THEN s.r3
           WHEN 'Actor'     THEN s.r4
           ELSE NULL
           END
  ORDER BY CASE s.Occupation
           WHEN 'Doctor'    THEN s.r1
           WHEN 'Professor' THEN s.r2
           WHEN 'Singer'    THEN s.r3
           WHEN 'Actor'     THEN s.r4
           ELSE NULL
           END