Получение значений из предыдущих строк с использованием пользовательской переменной в MySQL - PullRequest
0 голосов
/ 04 марта 2019

У меня есть таблица с именем "t1", моя таблица -

+------+------+
| c1   |  c2  |
+------+------+
|  1   |   12 |
|  2   |   13 |
|  3   |   14 |
+------+------+

Я хочу получить все строки вместе с предыдущими значениями col2.Я использую запрос: -

Select c1,@prev,@prev:=c2 from t1;

Я получаю следующий вывод -

+------+---------+------------+
| c1   |  @prev  | @prev:=c2  |
+------+---------+------------+
|  1   |   NULL  |    12      |
|  2   |   NULL  |    13      |
|  3   |   NULL  |    14      |
+------+---------+------------+

Я ожидал получить только NULL в первой строке.Но я получаю NULL во всех рядах.Пожалуйста, объясните, почему он дает NULL во всех строках.

Ответы [ 4 ]

0 голосов
/ 04 марта 2019

Причина появления значений 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-системах.

0 голосов
/ 04 марта 2019

Причина, по которой вы получаете все значения NULL, заключается в том, что вы не инициализируете переменную @prev.Вы можете инициализировать его с помощью оператора SET @prev := 0 или, если вам нужно получить этот результат только одним запросом, вы можете использовать CROSS JOIN для инициализации вашей переменной:

SELECT c1, @prev AS prev, @prev:=c2 AS c2
FROM t1
CROSS JOIN (SELECT @prev := 0) v;

Вывод:

c1  prev    c2
1   0       12
2   12      13
3   13      14

Если у вас нет для использования переменной, и ваши значения c1 являются последовательными, вы также можете получить тот же результат, используя LEFT JOIN:

SELECT t1.c1, t1.c2, t2.c2 AS prev
FROM t1
LEFT JOIN t1 t2 ON t2.c1 = t1.c1 - 1
ORDER BY t1.c1

Если ваши c1 значения могут быть не последовательными, вышеприведенный LEFT JOIN не будет работать должным образом.В этом случае вам нужно JOIN на значении c1, которое является наибольшим значением, которое меньше значения, которое должно быть JOIN ed:

SELECT t1.c1, t1.c2, t2.c2 AS prev
FROM t1
LEFT JOIN t1 t2 ON t2.c1 = (SELECT MAX(c1) 
                            FROM t1 t3 
                            WHERE t3.c1 < t1.c1)
ORDER BY t1.c1

В обоих случаях вывод одинаков:

c1  c2  prev
1   12  null
2   13  12
3   14  13

Демонстрация на dbfiddle

0 голосов
/ 04 марта 2019

Предыдущее значение можно получить с помощью подзапроса:

    SET @def = 0;

    Select 
      t.c1,
      coalesce(
        (select c2 from tablename where c1 < t.c1 order by c1 desc limit 1), 
        @def) prev_col,
      t.c2 cur_col
    from tablename t order by t.c1;

См. Демо Или:

SET @def = 0;

Select 
  t.c1,
  coalesce(
    (select max(c2) from tablename where c1 < t.c1), 
    @def) prev_col,
  t.c2 cur_col
from tablename t order by t.c1;

См. Демо

0 голосов
/ 04 марта 2019

Вы можете попробовать ниже -

ДЕМО

SET @prev=-1;
Select c1,@prev prev_col,@prev:=c2 cur_col
from t1 order by c1

ВЫХОД:

c1  prev_col    cur_col
1     -1          12
2     12          13
3     13          14
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...