使用MySQL对组中的行进行编号:它是如何工作的?

时间:2014-09-15 19:28:14

标签: mysql

关于如何使用MySQL对组内行进行编号有几篇好文章,但实际代码如何工作?我不清楚MySQL在下面的代码中首先评估的内容。

例如,在@yearqt := yearqt as bloc调用之前放置IF()会产生不同的结果,而且我不清楚s1子查询在初始化@变量时的作用:当MySQL在数据行中运行时,它们何时更新? order by语句是否在select之前运行?

以下代码为每个yearqt组选择三个随机记录。可能有其他方法可以做到这一点,但问题涉及代码如何工作,而不是我如何能够以不同的方式做到这一点,或者我是否可以更有效地做到这一点。谢谢。

 select * from (
   select customer_id , yearqt , a ,
   IF(@yearqt = yearqt , @rownum := @rownum + 1 , @rownum := 1) as rownum , 
   @yearqt := yearqt as bloc
   from 
   ( select customer_id , yearqt , rand(123) as a from tbl
     order by rand(123)
   ) a join ( select @rownum := 0 , @yearqt := '' ) s1
   order by yearqt
 ) s2 
 where rownum <= 3
 order by bloc

2 个答案:

答案 0 :(得分:0)

此问题与引擎检索SQL SELECT查询结果的方式有关。订单大致如下:

  1. 计算解释计划
  2. 使用计划指令计算集合并加入它们(FROM / JOIN阶段)
  3. 申请WHERE条款
  4. 应用GROUP BY / HAVING条款
  5. 申请ORDER BY条款
  6. 投影阶段:返回的每一行都是有序的,现在可以“显示”。
  7. 因此,关于变量,您现在理解为什么有子查询来初始化它们。此子查询仅在过程开始时评估一次。

    之后,项目阶段似乎按照您决定的顺序处理每个选定的属性,这就是为什么放置@yearqt := yearqt as bloc一个属性会改变下一个/上一个IF语句的结果的原因。由于每行将被投影一次,这意味着您对变量所做的任何工作都将完成与最终结果集中的行数相同的次数。

答案 1 :(得分:0)

此目的

join ( select @rownum := 0 , @yearqt := '' ) s1

是在语句执行开始时初始化用户定义的变量。因为这是外部查询的行源(MySQL称之为派生表),所以这将在外部查询运行之前执行。我们对这个查询返回的内容并不感兴趣,只是因为JOIN操作它会返回一行。

因此,可以从查询中省略此内联视图s1,并将其替换为在查询之前立即执行的几个SET语句:

SET @rownum := 0;
SET @yearqt := 0;

但是接下来我们要运行三个单独的语句,如果这些语句没有运行,我们会从查询中获得不同的输出,如果这些变量设置为其他值。通过在查询本身中包含它,它是一个单独的语句,我们删除对单独的SET语句的依赖。


这是真正开展工作的查询,仅限于在这种情况下重要的两个表达式

 SELEECT IF(@yearqt = t.yearqt , @rownum := @rownum + 1 , @rownum := 1) as rownum 
       , @yearqt := t.yearqt as bloc
    FROM ( ... ) t
   ORDER BY t.yearqt

使这个“工作”的一些关键点

MySQL按照它们在SELECT列表中出现的顺序处理SELECT列表中的表达式。

MySQL按照ORDER BY中指定的顺序处理行。

对每行计算对用户定义变量的引用,而不是在语句的开头计算一次。

请注意,MySQL参考手册指出此行为保证。 (因此,它可能会在将来的版本中发生变化。)

因此,对此的处理可以描述为

表示第一个表达式:

  • 将当前行的yearqt列的值与@yearqt用户定义变量的当前值进行比较
  • 设置@rownum用户定义变量的值
  • 返回结果集中的IF()表达式的结果

表示第二个表达式:

  • 将@yearqt用户定义变量的值设置为当前行中yearqt列的值
  • 返回结果集中的yearqt列的值

净效果是,对于每个处理的行,我们将yearqt列中的值与“之前”处理的行中的值进行比较,我们将保存当前值以与下一行进行比较。< / p>