在MySQL中使用用户定义的变量从上一行获取值

时间:2019-03-04 08:12:30

标签: mysql sql variables user-defined

我有一个名为“ 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 :(得分:1)

您可以在下面尝试-

DEMO

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

答案 1 :(得分:1)

获得所有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的{​​{1}}值是最高值,小于要c1设置为的值:

JOIN

在两种情况下,输出都是相同的:

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

Demo on dbfiddle

答案 2 :(得分:0)

您可以通过子查询获取先前的值:

    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;

请参见demo 或者:

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;

请参见demo

答案 3 :(得分:0)

记录为NULL的原因here

  

另一个问题是为变量分配值并读取   在同一非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);

请参见Demo

但是-在同一条语句(SET除外)中读写用户变量时-未定义行为。在文档中,您将找到以下语句:

  

还可以在语句中为用户变量分配值   除SET以外。 ( MySQL 8.0 中已弃用此功能 ,并且   可能会在后续版本中删除。)

     

...

     

作为一般规则,除了SET语句中的内容外,绝对不要   为用户变量分配一个值,并在同一变量内读取该值   声明。

     

...

     

对于其他语句,例如SELECT,您可能会得到结果   期望,但是不能保证

我已经用粗体标记了重要部分,因此您可以发现不建议以这种方式使用变量。

较新的版本(MySQL 8.0和MariaDB 10.2)现在支持window functions like LEAD() and LAG()。因此,您可以通过以下方式编写查询:

Select c1, c2, lag(c2) over (order by c1) as prev
from t1

Demo

这不仅对将来的MySQL版本可靠。它还将在许多(所有主要的)RDBM系统上工作。