MySQL子查询错误没有"传播"到外部查询

时间:2018-01-15 10:11:34

标签: mysql subquery

我一直在尝试追踪现有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 个答案:

答案 0 :(得分:2)

回答我自己的问题,现在我已经受到启发,可以玩一下。

首先,请注意,与评论相反,它似乎不是客户问题。是否是MySQL中的bug /功能/我不知道。但试图发现它肯定是一种令人担忧的行为。

首先,以下略有改动的代码 正确地返回错误:

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

在这里,我已将子查询中的列名更改为zzzChargeID,其中与作为外表中的列存在的ChargeID相同。现在存在"歧义",整个查询返回错误。

问题出现在:子查询按名称引用外部查询表中存在 的列,但子查询中是否存在 <: / p>

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

此处子查询尝试访问名为ChargeID的列,该列在其表中不存在但在外表中存在。 MySQL正在引用ChargeID的内部引用,指的是外表ChargeID列,这根本不是预期的!

然后结果取决于内部查询的WHERE子句是否在其表中找到任何匹配行:

  • 如果 (有一行BatchID = 3500)则为外部 查询从其charges表中返回所有行;
  • 如果 (有没有BatchID = 3500)然后是外部查询 从charges表返回 0行

结果就是:鉴于这种行为,人们应该从不敢写一个像这样的查询:

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

子查询中的col1未使用其表名限定。为了确保不会意外地选择子查询表中不存在但在外表中存在的列名,一个必须符合以下条件:

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

这非常令人担忧,因为现有的代码库可能在子查询引用中充满了这些不合格的列,这些列不会产生错误,而是会无声地产生不希望的结果。

如果有人知道是否有更好的解决方案,而不是相应地更改每个子查询的代码,我应该非常感激听到它。