MySQL中以下变量初始化样式有何不同?

时间:2012-11-05 06:02:31

标签: mysql sql select variable-declaration variable-initialization

我对涉及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 ||

那么这是不是意味着内部变量初始化会覆盖外部初始化并使后者更加冗余(因此在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初始化变量?

1 个答案:

答案 0 :(得分:11)

请务必阅读manual section on user variables

  

上述两个查询有哪些不同之处,以及两者中的哪一个在范围,效率,编码习惯,用例方面采用哪些?

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

  

那么这是不是意味着内部变量初始化会覆盖外部初始化并使后者更加冗余(因此在SELECT初始化它总是更好的做法?

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

  

行存储在哪里?它的范围和寿命是多少?

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

  

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

引自the manual

  

如果您引用尚未初始化的变量,则其值为NULL且字符串类型。

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

  

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

因此,在您的情况下,@name:=name部分可能会在@name <> name检查之前执行,导致您的所有rownum值都相同。因此,即使它现在有用,也无法保证它将来会起作用。

注意我一直对以这种方式使用用户变量持怀疑态度。我已经在评论手册中引用了上述警告,并提到了几个答案。我也问了一些关于Guarantees when using user variables to number rows的问题。其他用户更务实,因此更愿意使用看似有效的代码而不明确保证事情将继续按预期工作。