奇怪的事情。有一张桌子:
+-----------------+---------------+------+-----+---------+-------+
| 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客户端运行的。
答案 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