我需要生成按关闭百分比排名的员工列表。我构建了以下存储过程:
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
返回
| 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|
此查询适用于其预期目的,但在查看查询后,它看起来应该无法正确返回。这就是为什么我这么认为:
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|
为什么不是这样?
答案 0 :(得分:1)
在任何顺序中使用@variables并不严格地说是可靠的,但它是模仿MySQL仍然缺乏的窗口函数的常见“黑客”(计划用于v8.x,即仍在预发布中)。
这种“hack”依赖于ORDER BY与SELECT一起处理(而不是2个不同的步骤),它们比你预期的更加集成。 e.g。
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子句中从左到右(或从上到下)的表达式序列。因此有可能@per与计算@cnt的比较不是以书面sql中显示的方式完成的。在实践中,它“大部分时间”都有效,但可能不会。 (所以带上窗口功能!! )