首页 > 解决方案 > 在子查询中初始化 mysql 中的用户定义变量

问题描述

我正在阅读这个答案 How do you select every n-th row from mysql。因为我无法理解以下子查询中的初始化。

SELECT 
    @row := @row +1 AS rownum, [column name] 
FROM ( 
    SELECT @row :=0) r, [table name]

究竟是如何初始化的

选择 @row :=0

正在工作中?

表“r”和“表名”之间是否发生某种连接?

如果我将上述查询更改如下,性能会有什么不同吗?

设置@row = 0;

SELECT @row := @row +1 AS rownum, [列名] FROM [表名]

请分享你的想法。

标签: mysql

解决方案


使用两个语句,在单独的语句中初始化用户定义的变量将是等效的性能。

代替SET声明,我们可以做

SELECT @row : = 0

这将达到相同的结果,为用户定义的变量赋值@row。不同之处在于 MySQL 需要准备一个结果集以返回给客户端。SET我们使用不返回结果集的语句来避免这种情况。

对于两个单独的语句执行,发送额外语句会产生开销:解析标记、语法检查、语义检查……并将状态返回给客户端。这是少量的开销。我们不会注意到它onesie-twosie。

所以性能将是相同的。


我强烈建议放弃用于连接操作的老式逗号语法,而改用JOIN关键字。

考虑以下查询:

SELECT t.foo 
  FROM r 
 CROSS
  JOIN t
 ORDER BY t.foo 

当表r保证只包含一行时会发生什么?

该查询相当于:

SELECT t.foo 
  FROM t
 ORDER BY t.foo

我们可以使用 SELECT 查询来代替表或视图。考虑例如:

SELECT v.foo 
  FROM ( SELECT t.foo 
           FROM t
       ) v

还要考虑这个查询会发生什么:

SELECT @foo := 0

没有 FROM 子句(或 Oracle 样式FROM dual),因此查询将返回单行。SELECT 列表中的表达式被求值...常量值 0 被分配给用户定义的变量 @foo。

考虑这个查询:

SELECT 'bar'
  FROM ( SELECT @foo := 0 ) r

在外部查询运行之前,执行括号内的 SELECT。(MySQL 称它为“派生表”,但更一般地说,它是一个内联视图定义。)最终效果是常量 0 被分配给用户定义的变量,并返回单行。所以外部查询返回单行。

如果我们理解这一点,我们就有了理解这里发生的事情所需要的东西:

 SELECT t.mycol
   FROM ( SELECT @row := 0 ) r
  CROSS
   JOIN mytable t
  ORDER
     BY t.mycol

评估内联视图r,SELECT 返回单行,将值0分配给用户定义的变量@row。由于r保证返回单行,我们知道笛卡尔积(交叉连接)mytable将导致mytable. 有效地只产生mytable.


要回答未提出的问题:

在语句中进行初始化而不是单独的语句的好处是我们现在有一个独立的语句。它消除了依赖关系,即不需要单独执行SET语句来分配用户定义的变量。这也减少了到数据库的往返以准备和执行单独的语句。


推荐阅读