Ошибка подзапроса MySQL не "распространяется" на внешний запрос

Я пытался отслеживать ошибочное поведение в существующем коде MySQL (5.7, Linux). Я поражен/озадачен/недоволен поведением, которое я обнаружил, когда подзапрос возвращает ошибку:

SELECT * FROM charges WHERE ChargeID IN
 (SELECT ChargeID FROM history WHERE BatchID = 3500);

Вышеуказанный запрос возвращает 0 строк. Далее исследуем подзапрос:

SELECT ChargeID FROM history WHERE BatchID = 3500

оказывается ошибкой (что верно):

Error Code: 1054. Unknown column 'ChargeID' in 'field list'

Я понятия не имел, что подзапрос возвращал ошибку. Но внешний запрос просто возвращает 0 строк, "успешно".

Вот что я вижу в MySQL Workbench. Обратите внимание, как вывод успешно сообщает о возврате 0 строк для первого запроса при создании ошибки для второго запроса (который является подзапросом):

SELECT * FROM charges WHERE ChargeID IN
 (SELECT ChargeID FROM history WHERE BatchID = 3500);

SELECT ChargeID FROM history WHERE BatchID = 3500;


10:18:02 SELECT * FROM charges WHERE ChargeID IN (SELECT ChargeID FROM history WHERE BatchID = 3500) LIMIT 0, 1000 0 row(s) returned 0.0017 sec / 0.0000091 sec
10:18:02 SELECT ChargeID FROM history WHERE BatchID = 3500 LIMIT 0, 1000 Error Code: 1054. Unknown column 'ChargeID' in 'field list' 0.00020 sec

Почему MySQL не возвращает ошибку для внешнего запроса? (Я также ценю ссылку на документацию, объясняющую это поведение?) Как мне вернуть ошибку в таком случае? Как бы то ни было, для всех, кого я знаю, может быть много случаев в существующем коде с таким ошибочным подзапросом, если я не знаю ни одного из них...

1 ответ

Чтобы ответить на мой собственный вопрос, теперь, когда меня вдохновили немного поиграть.

Во-первых, обратите внимание, что, вопреки комментариям, это не проблема клиента. Является ли это ошибкой/функцией/предназначенной в MySQL, я не знаю. Но это, безусловно, тревожное поведение, чтобы попытаться обнаружить.

Во-первых, следующий слегка измененный код корректно возвращает ошибку:

SELECT * FROM charges WHERE ChargeID IN
 (SELECT zzzChargeID FROM history WHERE BatchID = 3500);

Здесь я изменил имя столбца в подзапросе на zzzChargeID, что не совпадает с именем ChargeID которое существует как столбец во внешней таблице. Теперь есть "двусмысленность", и весь запрос возвращает ошибку.

Проблема возникает, когда: подзапрос ссылается по имени на столбец, который существует во внешней таблице запросов, но не существует в подзапросе:

SELECT * FROM charges WHERE ChargeID IN
 (SELECT ChargeID FROM history WHERE BatchID = 3500);

Здесь подзапрос пытается получить доступ к столбцу с именем ChargeID, который не существует в его таблице, но существует во внешней таблице. MySQL берет внутреннюю ссылку на ChargeID ссылаясь на столбец ChargeID внешней таблицы, что совсем не так, как предполагалось!

Тогда результат зависит от того, находит ли WHERE внутреннего запроса какую-либо подходящую строку в своей таблице:

  • если оно (есть строка с BatchID = 3500), то внешний запрос возвращает все строки из его charges таблицы;
  • если это не так (нет строки с BatchID = 3500), то внешний запрос возвращает 0 строк из его charges таблицы.

Таким образом, результат: учитывая такое поведение, нельзя смело писать запрос, например:

SELECT col1, ... FROM table1 WHERE col1 IN
 (SELECT col1 FROM table2 ...)

col1 в подзапросе не имеет имени своей таблицы. Чтобы быть уверенным, что вы случайно не выбираете имя столбца, которое не существует в таблице подзапросов, но существует во внешней таблице, нужно иметь право:

SELECT col1, ... FROM table1 WHERE col1 IN
 (SELECT table2.col1 FROM table2 ...)

Это очень тревожно, так как потенциально существующая база кода может быть заполнена такими неквалифицированными столбцами в ссылках на подзапросы, которые не порождают ошибок, а молчат, производя нежелательные результаты.

Если кто-нибудь знает, есть ли лучшее решение, чем изменение каждого отдельного кода подзапроса, я должен быть очень благодарен его услышать.

licensed under cc by-sa 3.0 with attribution.