当使用变量和查询更改时,MySQL会重复结果

时间:2019-03-18 22:39:49

标签: mysql variables

奇怪的事情。有一张桌子:

+-----------------+---------------+------+-----+---------+-------+
| Field           | Type          | Null | Key | Default | Extra |
+-----------------+---------------+------+-----+---------+-------+
| date_time       | datetime(3)   | NO   | PRI | NULL    |       |

有数据:

+-------------------------+
| date_time               |
+-------------------------+
| 2017-01-02 00:00:00.000 |
| 2017-01-03 00:00:00.000 |
| 2017-01-04 00:00:00.000 |
| 2017-01-05 00:00:00.000 |
| 2017-01-06 00:00:00.000 |
| 2017-01-08 00:00:00.000 |
| 2017-01-09 00:00:00.000 |
| 2017-01-10 00:00:00.000 |
| 2017-01-11 00:00:00.000 |
| 2017-01-12 00:00:00.000 |
+-------------------------+
10 rows in set (0.00 sec)

如果我这样查询:

SELECT 
    @date_from := FIRST_VALUE (DATE(`date_time`)) OVER (
        ORDER BY `date_time`
    ) AS `Date From`,

    @date_to := LAST_VALUE(DATE(`date_time`)) OVER (
        ORDER BY `date_time`
        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
    ) AS `Date To`,

    @date_diff := TIMESTAMPDIFF(DAY, @date_from, @date_to) AS `Period [D]`

FROM 
    test
WHERE 
    `date_time` 
    BETWEEN 
        '2017-01-02' 
    AND 
        '2017-01-12'            
LIMIT 1;

我得到结果:

+------------+------------+------------+
| Date From  | Date To    | Period [D] |
+------------+------------+------------+
| 2017-01-02 | 2017-01-12 |         10 |
+------------+------------+------------+
1 row in set, 3 warnings (0.00 sec)

符合预期,但带有3条警告(每条句子相同):

  

警告| 1287 |在表达式中设置用户变量是   已过时,将在以后的版本中删除。考虑   备选方案:“ SET变量=表达式,...”或“ SELECT表达式”   INTO变量”

当我在查询结束时更改一个日期时:

WHERE 
    `date_time` 
    BETWEEN 
        '2017-01-02' 
    AND 
        '2017-01-10' -- Changed
LIMIT 1;

我得到的结果与以前相同(错误):

+------------+------------+------------+
| Date From  | Date To    | Period [D] |
+------------+------------+------------+
| 2017-01-02 | 2017-01-10 |         10 |
+------------+------------+------------+
1 row in set, 3 warnings (0.00 sec)

但是现在出现了有趣的部分:如果我回答执行相同的查询(结束日期为2017-01-10),则会得到正确的结果:

+------------+------------+------------+
| Date From  | Date To    | Period [D] |
+------------+------------+------------+
| 2017-01-02 | 2017-01-10 |          8 |
+------------+------------+------------+
1 row in set, 3 warnings (0.00 sec)

@date_diff稍后在其他语句中使用,但我切断了所有不相关的代码。

如何重写选择部分,这样就不会出现警告,为什么在第一次执行查询时却出现错误的句点(@date_diff),而在第二次查询时却出现错误?该怎么解决?

我正在使用MySQL 8,并且代码查询是从MySQL CLI客户端运行的。

1 个答案:

答案 0 :(得分:3)

这就是为什么他们要删除变量的这种用法。来自manual

  

涉及用户变量的表达式的求值顺序为   未定义。例如,不能保证SELECT @a, @a:=@a+1   首先评估@a,然后执行分配。

无法保证SELECT中变量赋值的执行顺序,因此第二个查询中似乎正在发生@date_diff之前< / em> @date_to的值已更新。解决此问题的最简单方法是删除变量:

SELECT 
    FIRST_VALUE(DATE(`date_time`)) OVER (ORDER BY `date_time`) AS `Date From`,
    LAST_VALUE(DATE(`date_time`)) OVER (ORDER BY `date_time` ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS `Date To`,
    TIMESTAMPDIFF(DAY, FIRST_VALUE(DATE(`date_time`)) OVER (ORDER BY `date_time`), LAST_VALUE(DATE(`date_time`)) OVER (ORDER BY `date_time` ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)) AS `Period [D]`
FROM test
WHERE `date_time` BETWEEN '2017-01-02' AND '2017-01-12'            
LIMIT 1

或使用子查询使其更整洁:

SELECT `Date From`, `Date To`, TIMESTAMPDIFF(DAY, `Date From`, `Date To`) AS `Period [D]`
FROM (SELECT 
        FIRST_VALUE(DATE(`date_time`)) OVER (ORDER BY `date_time`) AS `Date From`,
        LAST_VALUE(DATE(`date_time`)) OVER (ORDER BY `date_time` ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS `Date To`
      FROM test
      WHERE `date_time` BETWEEN '2017-01-02' AND '2017-01-12'            
      LIMIT 1) t