MySQL повторяет результаты при использовании переменных и изменении запроса - PullRequest
0 голосов
/ 19 марта 2019

Странная вещь.Есть таблица:

+-----------------+---------------+------+-----+---------+-------+
| 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 variable = expression, ...' или 'SELECT expression (s) INTO variable (s)'

И когда я изменяю в конце запроса одну дату:

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. и запросы кода запускаются из клиента CLI MySQL.

1 Ответ

3 голосов
/ 19 марта 2019

Вот почему они удаляют это использование переменных.Из руководства :

Порядок вычисления выражений с участием пользовательских переменных не определен.Например, нет гарантии, что SELECT @a, @a:=@a+1 сначала оценит @a, а затем выполнит присваивание.

Нет никакой гарантии относительно порядка выполнения присваиваний переменных в вашем SELECT,так что, по-видимому, происходит во втором запросе то, что @date_diff вычисляется до , значение @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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...