Почему MySQL 5.7 упорядочен по столбцу временного ранга?
Мне нужно сформировать список сотрудников, ранжированных по процентам закрытия. Я построил следующую хранимую процедуру:
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 обрабатывает SELECT до обработки ORDER BY. Таким образом, я предположил бы, что 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|
Почему это не так?
1 ответ
Использование @variables в любом порядке не является строго говоря надежным, но это обычное "хакерство" для имитации оконных функций, которых в MySQL все еще не хватает (планируется для v8.x, т.е. все еще в предварительной версии).
Этот "взлом" основан на том, что ORDER BY обрабатывается вместе с SELECT (не как 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
установит ранги, начиная с наименьшего значения процента, затем внешний порядок будет ставить наибольший процент первым.
Реальная проблема заключается в том, что последовательность выражений слева направо (или сверху вниз, как видно выше) не гарантируется. Таким образом, возможно, что сравнение @percent для вычисления @cnt выполняется не так, как показано в написанном sql. На практике это работает "большую часть времени", но не может. (Так что возьмите на себя оконные функции!!)