Причина появления значений NULL задокументирована здесь :
Другая проблема с присвоением значения переменной и чтением значения в том же операторе, отличном от SET, заключается в том, чтоТип результата по умолчанию для переменной основан на ее типе в начале оператора.
Поскольку @prev
не определено до того, как оно будет прочитано в первый раз, мы даже не можем сказать, какой типон имеет.
Теперь вы можете установить для него какое-то значение, например set @prev = 0;
.Но 0
может быть допустимым значением, поэтому вы, вероятно, хотите, чтобы оно было NULL для первой строки.set @prev = null;
не будет работать.Но вы можете сделать следующее:
set @prev = -1; -- define type as SIGNED
set @prev = null; -- set NULL
Теперь ваш запрос должен вернуть
c1 @prev @prev:=c2
1 null 12
2 12 13
3 13 14
Вы также можете определить тип и присвоить NULL в одном выражении:
set @prev = cast(null as signed);
См. Демонстрация
Однако - Когда вы читаете и пишете пользовательскую переменную в том же операторе (кроме SET) - поведение не определено.В документации вы найдете следующие операторы:
Также возможно присвоить значение пользовательской переменной в операторах, отличных от SET
.(Эта функция устарела в MySQL 8.0 и подлежит удалению в следующем выпуске.)
...
Как правило,кроме операторов SET, вы никогда не должны присваивать значение пользовательской переменной и читать значение в том же операторе.
...
Для других операторов:например, SELECT
, вы можете получить ожидаемые результаты, но это не гарантировано .
Я выделил важные части жирным шрифтом, так что вы можете видеть, чтоне рекомендуется использовать переменные таким образом.
Более новые версии (MySQL 8.0 и MariaDB 10.2) теперь поддерживают оконные функции, такие как LEAD()
и LAG()
.Таким образом, вы можете написать запрос следующим образом:
Select c1, c2, lag(c2) over (order by c1) as prev
from t1
Демо
Это не только надежно для будущих версий MySQL.Он также будет работать на многих (всех основных) RDBM-системах.