Странное поведение временного автоинкрементного столбца в MySQL против предложения HAVING - PullRequest
0 голосов
/ 10 февраля 2019

Предположим, у нас есть таблица с именем Test с одним столбцом col1 (Varchar (10)).Для примера предположим, что у него есть следующие данные:

col1
a
b
c
d

Теперь я хочу выбрать данные в col1 и добавить временный столбец автоинкремента, который мыпозвонит Ранг .Следующий запрос выполняет работу:

SELECT
(@cnt := @cnt + 1) AS Rank, Test.col1
FROM Test 
CROSS JOIN (SELECT @cnt := 0) AS tmp;

Результат -

Rank col1
1    a
2    b
3    c
4    d

Пока нет проблем.Теперь предположим, что нам нужно выбрать строки с рангом больше 1. Я делаю следующее:

SELECT
(@cnt := @cnt + 1) AS Rank,  Test.col1
FROM Test 
CROSS JOIN (SELECT @cnt := 0) AS tmp
having Rank > 1;

В результате получается

Rank col1
3    b
5    c
7    d

Обратите внимание, что первый элемент в столбце Ранг3, а не 2, как и следовало ожидать, естественно.Кто-то может указать на возможную причину этого?

Почему ранг (временное автоинкремент) переходит с 2 на 3?

Версия MySQL:5.6.32-78.1.

1 Ответ

0 голосов
/ 11 февраля 2019

Есть много неожиданных вещей, которые могут произойти при использовании переменных.Некоторые из них перечислены в руководстве :

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

Точные детали могут отличаться, но вв вашем случае ваш приращение оценивается дважды (один раз в select, один раз для having), поэтому он в основном ведет себя как

SELECT (@cnt := @cnt + 1) AS Rank,  Test.col1
FROM Test 
CROSS JOIN (SELECT @cnt := 0) AS tmp
having (@cnt := @cnt + 1) > 1;

Одним из способов решения этой проблемы является принудительное выполнение MySQL для предварительного вычисления выражения..

Правильное решение (насколько это возможно при использовании переменных), вероятно, таково:

select (
   SELECT (@cnt := @cnt + 1) AS Rank,  Test.col1
   FROM Test 
   CROSS JOIN (SELECT @cnt := 0) AS tmp
) x
where Rank > 1;

where Rank > 1 (без подзапроса) скорее всего то, что вы изначально намеревались сделать в любом случае.

Но вы можете сделать это и другими способами.В вашем примере вы должны иметь возможность использовать

SELECT (@cnt := @cnt + 1) AS Rank,  Test.col1
FROM Test 
CROSS JOIN (SELECT @cnt := 0) AS tmp
group by col1    
having Rank > 1;

Если col1 не является кандидатом в первичный ключ (например, уникальное значение, а не нуль), MySQL должен фактически оценить выражение, чтобы выполнить group by.С другой стороны, если col1 равен , например, первичный ключ, MySQL может оптимизировать group by и вы вернетесь к исходной ситуации - если он делает , то можетзависит от вашей версии MySQL, но Afaik MySQL 5.6 должен делать это.

Примечание: поскольку rank стало ключевым словом в MySQL 8, вы не должны использовать его в качестве псевдонима в случае, если вы когда-либо намереваетесь выполнить обновление.

...