为什么MySQL 5.7 order by会影响临时排名列?
Why does MySQL 5.7 order by affect temporary rank column?
我需要生成一份按关闭百分比排序的员工列表。我构建了以下存储过程:
SET @cnt = 0;
SET @percent = 2.0;
SELECT
CASE
WHEN stats.close/(stats.open+stats.close) = @percent THEN @cnt
ELSE (@cnt := @cnt + 1)
END rank,
stats.employee,
stats.close,
stats.open,
(@percent := stats.close/(stats.open+stats.close)) percent
FROM stats
WHERE stats.date = CURDATE()
ORDER BY percent
它Returns
| Rank | Employee | Close | Open | Percent|
| 1| Smith| 9| 1| 0.90|
| 2| Jones| 75| 25| 0.75|
| 3| Zed| 1| 9| 0.10|
| 3| Adams| 10| 90| 0.10|
此查询符合预期目的,但在查看查询后,它看起来不应该 return 正确。这就是我这么认为的原因:
MySQL 在处理 ORDER BY 之前处理 SELECT。因此,我假设 MySQL 会按照它决定从数据库中出来的任何顺序分配排名,然后对结果集进行排序。我希望它看起来像这样:
| Rank | Employee | Close | Open | Percent|
| 3| Smith| 9| 1| 0.90|
| 2| Jones| 75| 25| 0.75|
| 4| Zed| 1| 9| 0.10|
| 1| Adams| 10| 90| 0.10|
为什么不是这样?
以任何顺序使用@variables 严格来说并不可靠,但它是一种常见的 "hack" 来模仿 MySQL 仍然缺乏的 window 功能(计划用于 v8.x 即仍处于预发布阶段)。
此 "hack" 依赖于与 SELECT 一起处理的 ORDER BY(不是作为 2 个不同的步骤)它们比您预期的更集成。例如
SELECT *
FROM (SELECT CASE
WHEN stats.close / ( stats.open + stats.close ) = @percent THEN
@cnt
ELSE ( @cnt := @cnt + 1 )
end rank,
stats.employee,
stats.close,
stats.open,
( @percent := stats.close / ( stats.open + stats.close ) )
percent
FROM stats
CROSS JOIN (@percent := 0 x, @cnt :=0 y) vars
WHERE stats.date = Curdate()
ORDER BY percent ASC) d
ORDER BY percent DESC
内部顺序 percent ASC
将设置从最低百分比值开始的排名,然后外部顺序将最高百分比排在第一位。
真正的问题是 select 子句中的表达式顺序从左到右(或如上所示从上到下)无法保证。所以有可能@percent比较计算@cnt没有按照写的sql中的方式进行。在实践中它有效 "most of the time",但可能无效。 (所以启用 window 功能!!)
我需要生成一份按关闭百分比排序的员工列表。我构建了以下存储过程:
SET @cnt = 0;
SET @percent = 2.0;
SELECT
CASE
WHEN stats.close/(stats.open+stats.close) = @percent THEN @cnt
ELSE (@cnt := @cnt + 1)
END rank,
stats.employee,
stats.close,
stats.open,
(@percent := stats.close/(stats.open+stats.close)) percent
FROM stats
WHERE stats.date = CURDATE()
ORDER BY percent
它Returns
| Rank | Employee | Close | Open | Percent|
| 1| Smith| 9| 1| 0.90|
| 2| Jones| 75| 25| 0.75|
| 3| Zed| 1| 9| 0.10|
| 3| Adams| 10| 90| 0.10|
此查询符合预期目的,但在查看查询后,它看起来不应该 return 正确。这就是我这么认为的原因:
MySQL 在处理 ORDER BY 之前处理 SELECT。因此,我假设 MySQL 会按照它决定从数据库中出来的任何顺序分配排名,然后对结果集进行排序。我希望它看起来像这样:
| Rank | Employee | Close | Open | Percent|
| 3| Smith| 9| 1| 0.90|
| 2| Jones| 75| 25| 0.75|
| 4| Zed| 1| 9| 0.10|
| 1| Adams| 10| 90| 0.10|
为什么不是这样?
以任何顺序使用@variables 严格来说并不可靠,但它是一种常见的 "hack" 来模仿 MySQL 仍然缺乏的 window 功能(计划用于 v8.x 即仍处于预发布阶段)。
此 "hack" 依赖于与 SELECT 一起处理的 ORDER BY(不是作为 2 个不同的步骤)它们比您预期的更集成。例如
SELECT *
FROM (SELECT CASE
WHEN stats.close / ( stats.open + stats.close ) = @percent THEN
@cnt
ELSE ( @cnt := @cnt + 1 )
end rank,
stats.employee,
stats.close,
stats.open,
( @percent := stats.close / ( stats.open + stats.close ) )
percent
FROM stats
CROSS JOIN (@percent := 0 x, @cnt :=0 y) vars
WHERE stats.date = Curdate()
ORDER BY percent ASC) d
ORDER BY percent DESC
内部顺序 percent ASC
将设置从最低百分比值开始的排名,然后外部顺序将最高百分比排在第一位。
真正的问题是 select 子句中的表达式顺序从左到右(或如上所示从上到下)无法保证。所以有可能@percent比较计算@cnt没有按照写的sql中的方式进行。在实践中它有效 "most of the time",但可能无效。 (所以启用 window 功能!!)