一尘不染

使用用户变量对行进行编号时的保证

sql

使用用户变量为行编号

我经常在SO上找到答案,建议使用用户变量对某些事物进行编号。也许最清楚的例子是从给定结果集中选择第二行的查询。(此问题和查询与此答案相似,但实际上是答案触发了此问题)。

SELECT *
FROM (SELECT *, (@row := @row + 1) AS rownum
      FROM (SELECT @row := 0) AS init, tablename
      ORDER BY tablename.ordercol
     ) sub
WHERE rownum % 2 = 1

这种方法似乎确实可以正常工作。

注意的理由

另一方面,MySQ文档指出:

通常,永远不要为用户变量分配值,并且不要在同一条语句中读取该值。您可能会得到预期的结果,但这不能保证。涉及用户变量的表达式的求值顺序是不确定的,并且可能会根据给定语句中包含的元素而改变;此外,在MySQL服务器的发行版之间,不能保证此顺序相同。

核心问题

因此,我的问题不是不是如何使用当前服务器来实现这种排序,而是要 确保 使用用户变量的建议解决方案是否可以 保证
在所有(合理)情况下以及对于所有将来的MySQL版本都可以使用。

通过 鈥済鈥uarantees
我的意思是权威的来源,如MySQL文档或与一些标准的MySQL要求的一致性。缺乏这种权威性的答案,可能会引用其他资源,例如常用教程或MySQL源代码的一部分。通过
鈥渨兽人鈥 我的意思是分配会按顺序执行,每个结果行一次,并在由产生的顺序的事实ORDER BY一致。

中断查询的示例

举个例子,失败很容易发生:

SELECT *
FROM (SELECT *, (@row := @row + 1) AS rownum
      FROM (SELECT @row := 0) AS init, tablename
      HAVING rownum > 0
      ORDER BY tablename.ordercol
     ) sub
WHERE rownum % 2 = 1

SQL Fiddle上当前安装的MySQL
5.5.27上将产生空结果。原因似乎是该HAVING条件导致rownum表达式两次被求值,所以最终结果将只有偶数。我对幕后发生的事情有一个了解,但我并没有断言带有的查询HAVING是很有意义的。我只想证明在有效的代码和看起来非常相似但会中断的代码之间有一条很好的界限。


阅读 126

收藏
2021-03-10

共1个答案

一尘不染

您误读了该声明。使用多个变量时,它与SELECT列表中表达式的顺序有关。
如前所述,在此单变量语句上的ORDER BY具有保证的顺序,直到MySQL的当前版本为止,并且该文本中没有任何内容表明它将改变。

但是 保证未来 吗?谁知道。


关于 中断查询 ,您再次误解了MySQL的工作方式。让我们分解一下您的查询。注意手册中的这一声明

在SELECT语句中,每个选择表达式仅在发送到客户端时才进行求值。这意味着在HAVING,GROUP BY或ORDER
BY子句中,引用在选择表达式列表中分配了值的变量不能按预期工作

查询的处理顺序大致

FROM / JOIN
WHERE / ON
GROUP BY / ROLLUP
HAVING
UNION
SELECT
ORDER BY
@variable resolution

您的“中断”查询尝试在同一级别使用WITHIN变量,这与对列别名使用WHERE /
HAVING子句几乎一样有罪。这就是为什么您永远不会在同一查询级别上看到使用该变量的基于MySQL变量的row_numbering解决方案的原因,它始终在子查询中。外部查询可以视为client内部查询的内部查询,在该阶段已呈现变量/占位符表达式。根据您的论点,您可以使用直接涉及@row的WHERE子句轻松地将其破坏(是的,
它将 运行!)。

2021-03-10