MySQL разрешает подзапросы без оператора IN или оператора сравнения в предложениях WHERE.Как это работает? - PullRequest
0 голосов
/ 24 сентября 2019

РЕДАКТИРОВАТЬ: Я неправильно упростил запросы в исходной версии.этот запрос был тем, что я имел в виду.

SELECT *
FROM A
LEFT JOIN B ON A.seq = B.seq
WHERE (select max(B.data) where B.data is not null group by B.seq);

enter image description here

Я оставлю неверную версию, поскольку она также помогла мне в скалярном подзапросе.


Мой коллега спросил, почему этот оператор работает в MySQL.

SELECT *
FROM anytable
WHERE (SELECT 'asdf')

Это возвращает 0 строк.Я упростил запрос, но в основном предложение where содержит подзапрос без IN или оператора сравнения;просто сама.Мы ожидали, что он выдаст ошибку о синтаксисе SQL, как и SQL Server.Но MySQL этого не сделал.

Интересно, что если я изменю запрос, как показано ниже, запрос вернет все строки из anytable.

SELECT *
FROM anytable
WHERE (SELECT 1);

Я не смог найти документацию по этому вопросу.Как это работает?

Ответы [ 2 ]

3 голосов
/ 24 сентября 2019

Это ответ на оригинальную версию вопроса.

Когда вы делаете:

WHERE (SELECT 'asdf')

У вас есть скалярный подзапрос.Это эквивалентно:

WHERE 'asdf'

Теперь MySQL обрабатывает логические значения как целые числа и наоборот, с 0 для false и всем остальным как true.MySQL не рассматривает строки как логические значения, поэтому он решает преобразовать значение в число, используя неявное преобразование.

Неявное преобразование преобразует все начальные цифры.Их нет, поэтому значение конвертируется в 0.Вуаля!Он считается ложным.

Когда вы используете WHERE (SELECT 1), сохраняется та же логика, за исключением того, что значение уже является числом.И это 1, поэтому оно считается истинным.

0 голосов
/ 24 сентября 2019
SELECT *
FROM A
LEFT JOIN B ON A.seq = B.seq
WHERE (select max(B.data) where B.data is not null group by B.seq);

Это не может быть вашим реальным запросом.Это не действительный SQL.Вот что я получаю при тестировании:

ОШИБКА 1064 (42000): у вас ошибка в синтаксисе SQL;проверьте руководство, соответствующее вашей версии сервера MySQL, на предмет правильного синтаксиса, который можно использовать рядом со значением «где B.data не является пустой группой по B.seq)» в строке 4

Без сомнения, поскольку это не разрешено закономсинтаксис для использования предложения where или любого следующего за ним предложения, если у вас нет предложения from для определения ссылки на таблицу.


Тест, который я провел выше, был в MySQL 5.6,и я подтвердил, что он возвращает ту же ошибку при запуске запроса в MySQL Workbench.

Однако MySQL 8.0 поддерживает новый синтаксис.Вы можете использовать WHERE и другие предложения без предложения FROM.Я не осознавал этого раньше.

Я извиняюсь за снисходительность, обвиняя вас в том, что вы не проверяете ошибки.

Вот демонстрационная версия:

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.17    |
+-----------+
1 row in set (0.00 sec)

mysql> select 42;
+----+
| 42 |
+----+
| 42 |
+----+
1 row in set (0.00 sec)

mysql> select 42 where true;
+----+
| 42 |
+----+
| 42 |
+----+
1 row in set (0.00 sec)

mysql> select 42 where false;
Empty set (0.00 sec)

MySQL всегда поддерживаетсявыбрать одну строку, указав набор фиксированных выражений в списке выбора.Теперь с новым синтаксисом вы можете сделать так, чтобы эта строка оставалась или отфильтровывалась в зависимости от того, верно ли условие WHERE или нет.

В вашем запросе подзапрос оценивается для каждой строкивнешний запрос, поэтому столбцы B.data и B.seq в подзапросе имеют одно значение при каждом запуске подзапроса.Как будто вы сделали постоянное выражение.GROUP BY не имеет значения, так как в любом случае есть только одно значение для B.seq.

Гордон Линофф прав, что предложение WHERE внешнего запроса будет либо ложным, если max(B.data) равно 0, либоистина, если это не ноль.Если условие подзапроса WHERE равно false, то подзапрос не вернет ни одной строки, что ведет себя так же, как если бы он вернул false.

mysql> SELECT * FROM A LEFT JOIN B ON A.seq = B.seq WHERE (select true where true);
+-----+------+------+
| seq | seq  | data |
+-----+------+------+
|   1 |    1 | data |
+-----+------+------+
1 row in set (0.00 sec)

mysql> SELECT * FROM A LEFT JOIN B ON A.seq = B.seq WHERE (select true where false);
Empty set (0.00 sec)

Или, если max(b.data) возвращает значение, которое интерпретируется какноль / ложь, это также заставляет подзапрос оценивать как ложный, поэтому внешний запрос не возвращает строк.

mysql> SELECT * FROM A LEFT JOIN B ON A.seq = B.seq WHERE (select 0 where true);
Empty set (0.00 sec)

mysql> SELECT * FROM A LEFT JOIN B ON A.seq = B.seq WHERE (select 'abc' where true);
Empty set, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+-----------------------------------------+
| Level   | Code | Message                                 |
+---------+------+-----------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'abc' |
+---------+------+-----------------------------------------+
...