Гарантии при использовании пользовательских переменных для номера строк

Использование переменных пользователя для номера строк

Я часто нахожу здесь ответы на 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 Server.

Основной вопрос

Поэтому мой вопрос заключается не в том, как добиться такого упорядочения с использованием текущих серверов, а вместо того, гарантируется ли предлагаемое решение с использованием пользовательских переменных во всех (разумных) обстоятельствах и для всех будущих версий MySQL.

Под «гарантиями» я имею в виду такие авторитетные источники, как документация MySQL или некоторые стандартные требования к MySQL. Не имея таких авторитетных ответов, вместо них могут быть указаны другие источники, такие как часто используемые учебники или части исходного кода MySQL. Под «works» я подразумеваю тот факт, что назначения будут выполняться последовательно, один раз в строке результата и в порядке, вызванном строкой 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 

приведет к пустым результатам в MySQL 5.5.27, установленном в настоящее время на SQL Fiddle . Причина в том, что условие HAVING приводит к rownum выражение rownum получает оценку дважды, поэтому конечный результат будет иметь только четные числа. У меня есть идея, что происходит за кулисами, и я не утверждаю, что запрос с HAVING имеет смысл. Я просто хочу продемонстрировать, что существует тонкая грань между кодом, который работает, и кодом, который выглядит очень похожим, но ломается.

Вы неправильно читаете заявление. Он относится к порядку выражений в списке SELECT при использовании нескольких переменных.
Как представлено, ORDER BY в этом операторе с одной переменной имеет гарантированный порядок до текущей версии MySQL, и ничто в этом тексте не предполагает, что он изменится.

Но гарантировать будущее ? Кто знает.


Что касается разбивающего запроса , вы снова неправильно поняли, как работает MySQL. Давайте раскроем ваш запрос. Обратите внимание на это заявление в руководстве

В инструкции SELECT каждое выражение select оценивается только при отправке клиенту. Это означает, что в предложении HAVING, GROUP BY или ORDER BY, ссылаясь на переменную, которой назначено значение в списке выражений выбора, не работает должным образом

Порядок обработки запросов примерно

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

Ваш «сломанный» запрос пытается использовать переменную WITHIN на том же уровне, что примерно так же грешно, как использование предложения WHERE / HAVING для псевдонима столбца. Вот почему вы никогда не увидите решения на основе строки row_numbering на основе переменных, используя переменную на одном уровне запросов, она всегда находится в подзапросе. Внешний запрос можно рассматривать как client внутреннего запроса, на каком этапе визуализировалось выражение переменной / placeholder. По вашему аргументу вы можете так же легко сломать его, используя предложение WHERE, связанное с @row напрямую (да, это будет работать!).