6

我对涉及 MySQL 中变量声明的查询相当陌生。我见过各种风格,但我并不完全清楚它们的实际作用。我对这些实际上做了什么有疑问。

1)

set @row:=0;
SELECT name, @row:=@row + 1 AS rownum
FROM animal

2)

SELECT name, @row:=@row + 1 AS rownum
FROM (SELECT @row:= 0) c, animal

两者返回相同:

  name rownum
|| cat || 1 ||
|| cat || 2 ||
|| dog || 3 ||
|| dog || 4 ||
|| dog || 5 ||
|| ant || 6 ||

上述两个查询有什么区别?两者在范围、效率、编码习惯、用例方面有什么区别?

3)现在如果我这样做:

set @row:=0;
SELECT name, @row:=@row + 1 AS rownum
FROM (SELECT @row:= 123) c, animal

我明白了

  name  rownum
|| cat || 124 ||
|| cat || 125 ||
|| dog || 126 ||
|| dog || 127 ||
|| dog || 128 ||
|| ant || 129 ||

那么这是否意味着内部变量初始化会覆盖外部初始化并因此使后者变得多余(因此在 a 中进行初始化总是更好的做法SELECT

4)如果我只是这样做:

SELECT name, @row:=@row + 1 AS rownum
FROM animal

我明白了

   name  rownum
|| cat || NULL ||
|| cat || NULL ||
|| dog || NULL ||
|| dog || NULL ||
|| dog || NULL ||
|| ant || NULL ||

我可以理解,因为row没有初始化。但是,如果我运行任何其他查询(可能是变量row正在初始化?)我看到row每次运行上述查询时变量都会递增。那就是它在第一次运行时给了我结果:

  name rownum
|| cat || 1 ||
|| cat || 2 ||
|| dog || 3 ||
|| dog || 4 ||
|| dog || 5 ||
|| ant || 6 ||

然后在重新运行时产生

  name rownum
|| cat || 7  ||
|| cat || 8  ||
|| dog || 9  ||
|| dog || 10 ||
|| dog || 11 ||
|| ant || 12 ||

那么是row存储在某个地方吗?它的范围和寿命是多少?

5)如果我有这样的查询:

SELECT (CASE WHEN @name <> name THEN @row:=1 ELSE @row:=@row + 1 END) AS rownum, 
       @name:=name AS name
FROM animal

这总是产生正确的结果:

rownum  name
|| 1 || cat ||
|| 2 || cat ||
|| 1 || dog ||
|| 2 || dog ||
|| 3 || dog ||
|| 1 || ant ||

那么这是否意味着它并不总是需要在顶部或SELECT根据查询初始化变量?

4

1 回答 1

11

请务必阅读有关用户变量的手册部分

上述两个查询有什么区别?两者在范围、效率、编码习惯、用例方面有什么区别?

查询 1) 使用多个语句。因此,它可以依赖这些语句的执行顺序,确保变量在递增之前设置。
另一方面,查询 2) 在嵌套子查询中进行初始化。这会将整个事情变成一个查询。您不会冒险忘记初始化。但是代码更多地依赖于 mysql 服务器的内部工作,特别是它会在开始计算外部查询的结果之前执行子查询这一事实。

那么这是否意味着内部变量初始化会覆盖外部初始化并因此使后者变得多余(因此在 a 中进行初始化总是更好的做法SELECT

这不是关于内部和外部,而是关于顺序:子查询在 之后执行SET,因此它会简单地覆盖旧值。

那么行是否存储在某个地方?它的范围和寿命是多少?

用户变量是服务器连接的本地变量。因此任何其他过程都不会受到设置的影响。即使是同一个进程也可能维护多个连接,并具有独立的用户变量设置。一旦连接关闭,所有变量设置都将丢失。

那么这是否意味着它并不总是需要根据查询在顶部或 SELECT 中初始化变量?

引用手册

如果你引用一个没有被初始化的变量,它的值NULL和类型是字符串。

所以你可以在初始化之前使用一个变量,但是你必须小心你实际上可以NULL以合理的方式处理结果值。但是请注意,您的查询 5) 遇到手册中明确说明的另一个问题:

作为一般规则,您永远不应为用户变量赋值并在同一语句中读取该值。您可能会得到预期的结果,但这不能保证。涉及用户变量的表达式的求值顺序是未定义的,并且可能会根据给定语句中包含的元素而改变;此外,不保证此顺序在 MySQL 服务器的版本之间是相同的。在SELECT @a, @a:=@a+1, ...中,您可能认为 MySQL 会@a先评估,然后再进行赋值。但是,更改语句(例如,通过添加GROUP BYHAVINGORDER BY子句)可能会导致 MySQL 选择具有不同评估顺序的执行计划。

因此,在您的情况下,该@name:=name部分可能会在@name <> name检查之前执行,从而导致您的所有rownum值都相同。因此,即使它现在确实有效,也不能保证它将来会有效。

请注意,我一直对以这种方式使用用户变量持怀疑态度。我已经在对几个答案的评论中引用了手册中的上述警告。在使用用户变量对行数进行编号时,我还提出了一些问题,例如关于Guarantees 的问题。其他用户更务实,因此更愿意使用看似可以正常工作的代码,但没有明确保证事情将继续按预期工作。

于 2012-11-08T23:18:53.470 回答