У меня есть эта таблица в моей базе данных:
table t
id | a | b | date_x
1 | 81 | 12 | 2018-03-16
2 | 9 | 54 | 2025-04-21
3 | 81 | 67 | 2018-03-16
4 | 763 | 81 | 2018-03-16
5 | 90 | 22 | 2025-12-08
date_x имеет тип DATE
Я хотел бы выбрать строки, где a = 81 или b = 81, а date_x до 2019-05-28.
Поэтому я выполняю следующий запрос в MySQL Workbench:
SELECT * FROM t
WHERE a = '81' OR b = '81'
AND date_x > '2019-05-28';
Вот что я получаю:
1 | 81 | 12 | 2018-03-16
3 | 81 | 67 | 2018-03-16
Я ожидаю, что 2018-03-16 не позднее 2019-05-28 . Более того, почему вернулось только 2 строки? В столбце date_x есть еще один с той же датой.
Этот запрос возвращает то же самое:
SELECT * FROM t
WHERE a = '81' OR b = '81'
AND date_x > str_to_date('2019-05-28', '%Y-$m-%d');
Я выполнил следующий запрос для отладки:
SELECT * FROM t
WHERE a = '81' OR b = '81'
AND date_x < '2019-05-28';
а также
SELECT * FROM t
WHERE a = '81' OR b = '81'
AND date_x < str_to_date('2019-05-28', '%Y-$m-%d');
то оба возвращаются, как и ожидалось:
1 | 81 | 12 | 2018-03-16
3 | 81 | 67 | 2018-03-16
4 | 763 | 81 | 2018-03-16
Я прочитал следующий вопрос/ответ, но мне все еще чего-то не хватает:
Любой намек? Спасибо
Ваш запрос имеет вид
SELECT * FROM t WHERE condition_a OR condition_b AND condition_c
Оператор AND
связывает сильнее, чем OR
, поэтому в итоге вы получите
SELECT * FROM t WHERE condition_a OR (condition_b AND condition_c)
Отсюда я думаю и путаница. Ничего общего с датами как таковыми.
Вы должны заключить select
в скобки. Благодаря этому вы получаете WHERE a = '81' OR (b = '81' AND date_x >'2019-05-28')
.
SELECT * FROM t
WHERE (a = '81' OR b = '81')
AND date_x > '2019-05-28';
Это на пределе очевидности. Спасибо за помощь в фокусировке в нужную точку
Переписать в
SELECT * FROM t
WHERE a = '81' AND date_x > '2019-05-28'
UNION ALL
SELECT * FROM t
WHERE b = '81' AND date_x > '2019-05-28'
может иметь больше смысла, особенно когда вы рассматриваете производительность, рассмотрите эту структуру таблицы и индексацию.
CREATE TABLE t (
`id` INTEGER,
`a` INTEGER,
`b` INTEGER,
`date_x` VARCHAR(10)
, INDEX(a, date_x)
, INDEX(b, date_x)
);
Так как
SELECT * FROM t
WHERE (a = '81' OR b = '81')
AND date_x > '2019-05-28';
не может использовать индексы, см. демо и, скорее всего, в конечном итоге сканирует полные файлы таблиц/индексов.
Поскольку оптимизатор MySQL основан на затратах, я также включил это, чтобы убедиться, что ПОЛНОЕ сканирование для первого запроса не было просто вызвано слишком малым количеством записей..
Но ясно, что второй запрос имеет более стабильный план.
База данных Oracle, я считаю, делает OR
для UNION ALL
перезаписи непосредственно в этом оптимизаторе, если она проиндексирована. Поскольку MySQL принадлежит Oracle, я надеюсь, что они также добавят эту оптимизацию в MySQL.
Это на пределе очевидности. Спасибо за помощь в фокусировке в нужную точку