我对涉及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
初始化变量?
答案 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 BY
,HAVING
或ORDER BY
子句)可能会导致MySQL选择具有不同评估顺序的执行计划。
因此,在您的情况下,@name:=name
部分可能会在@name <> name
检查之前执行,导致您的所有rownum
值都相同。因此,即使它现在有用,也无法保证它将来会起作用。
注意我一直对以这种方式使用用户变量持怀疑态度。我已经在评论手册中引用了上述警告,并提到了几个答案。我也问了一些关于Guarantees when using user variables to number rows的问题。其他用户更务实,因此更愿意使用看似有效的代码而不明确保证事情将继续按预期工作。