【问题标题】:IN subquery's WHERE condition affects main query - Is this a feature or a bug?IN 子查询的 WHERE 条件影响主查询 - 这是功能还是错误?
【发布时间】:2013-12-16 04:17:46
【问题描述】:

假设有两个表:

Table A: A1, A2, A_Other
Table B: B1, B2, B_Other

在以下示例中, is something 是根据固定值检查的条件,例如 = 'ABC' < 45

我写了如下查询(1)

Select * from A
Where A1 IN (
    Select Distinct B1 from B
    Where B2 is something
    And A2 is something
);

我真正想写的是(2)

Select * from A
Where A1 IN (
    Select Distinct B1 from B
    Where B2 is something
)
And A2 is something;

奇怪的是,两个查询都返回了相同的结果。在查看查询1说明计划时,看起来像执行子查询时,因为条件A2 is something不适用于子查询,它是deferred 用作主要查询结果的过滤器。

我通常认为查询 1 会失败,因为子查询本身会失败:

Select Distinct B1 from B
Where B2 is something
And A2 is something; --- ERROR:  column "A2" does not exist

但我发现情况并非如此,Postgres 将不适用的子查询条件推迟到主查询。

这是标准行为还是 Postgres 异常? 这在哪里记录,这个功能叫什么?

另外,我发现如果我在表 B 中添加一列 A2,则只有查询 2 可以按最初的预期工作。在这种情况下,查询 2 中的引用 A2 仍将引用 A.A2,但查询 1 中的引用将引用新列 B.A2,因为它现在可以直接在子查询中应用。

【问题讨论】:

  • 这是一个相关子查询。
  • 这似乎不对,因为我没有真的使用主查询中的值来过滤子查询中的结果(这是相关子查询所做的)。此处的过滤器仍应用于主查询。但是,是的,可能就是这样。 顺便说一句,一个详细的答案解释正在发生的事情会很好。
  • A2 是什么东西或 B2 是什么东西不清楚。这取决于那个。我认为凭借一些记录,您可以从两个查询中获得相同的结果。
  • @KumarHarsh 添加了一个解释该部分的注释。这是对固定值的简单检查。另外,令人费解的部分是不是两个查询都返回相同的结果,而是第一个没有失败。
  • 关联子查询 1) 使用来自外部查询的值,并且 2) 对外部查询中评估的每一行执行一次。这两个功能可能与过滤结果有关,也可能无关。 (相关子查询可以出现在 SELECT 子句中。)

标签: sql postgresql subquery postgresql-9.1 in-subquery


【解决方案1】:

这是一个很好的问题,很多人都遇到过,但都懒得停下来看看。

您正在做的是在WHERE 子句中编写子查询;不是FROM 子句中的内联视图。有区别。

当您在SELECTWHERE 子句中编写子查询时,您可以访问主查询的FROM 子句中的表。这不仅发生在 Postgres 中,而且是一种标准行为,可以在所有领先的 RDBMS 中观察到,包括 Oracle、SQL Server 和 MySQL。

当您运行第一个查询时,优化器会查看您的整个查询并确定何时检查哪些条件。正是优化器的这种行为,您看到条件延迟到主查询,因为优化器发现在主查询本身中评估此条件会更快,而不会影响最终结果。 p>

如果只运行子查询,注释掉主查询,必然会在你提到的位置返回错误,因为没有找到所引用的列。

在您的最后一段中,您提到您在表tableB 中添加了一个列A2。你观察到的是对的。这是因为隐含的参考现象。如果您不提及列的表别名,则数据库引擎会首先在子查询的FROM 子句中的表中查找该列。仅当在那里找不到该列时,才会引用主查询中的表。如果您使用以下查询,它仍然会返回相同的结果:

Select * from A aa -- Check the alias
Where A1 IN (
    Select Distinct B1 from B bb
    Where B2 is something
    And aa.A2 is something -- Check the reference
);

也许您可以在 Korth 的关系数据库一书中找到更多信息,但我不确定。我刚刚根据我的观察回答了你的问题。我知道会发生这种情况以及为什么。我只是不知道如何为您提供进一步的参考。

【讨论】:

    【解决方案2】:

    相关子查询:-如果子查询的结果取决于其父查询表的列的值,则子查询称为相关子查询。这是标准行为,而不是错误。

    相关查询所依赖的列不必包含在父查询的选定列列表中。

    Select * from A
    Where A1 IN (
        Select Distinct B1 from B
        Where B2 is something
        And A2 is something
    );
    

    A2 是表 A 的列,父查询在表 A 上。这意味着 A2 可以在子查询中引用。上面的查询可能比下面的查询慢。

    Select * from A
    Where A2 is something And A1 IN (
        Select Distinct B1 from B
        Where B2 is something
    );
    

    这是因为循环引用了父查询中的 A2。这取决于要获取数据的条件。如果子查询类似于

    Select Distinct B1 from B
    Where B2 is A2
    

    我们必须引用父查询列。或者,我们可以使用连接。

    【讨论】:

      【解决方案3】:

      您已经解释了为什么WHERE 子句中的相关子查询可以引用FROM 列表中表中的所有列。

      除此之外,使用JOINEXISTS 半联接通常比相关子查询快得多。我会重写这个 100% 等效 查询:

      SELECT a.*
      FROM   a
      JOIN   (
         SELECT DISTINCT b1
         FROM   b
         WHERE  b2 is something
         ) b ON b.b1 = a.a1 
      WHERE  a.a2 is something
      

      或者,更好:

      SELECT *
      FROM   a
      WHERE  EXISTS (
         SELECT 1 
         FROM   b
         WHERE  b.b1 = a.a1 
         AND    b.b2 is something
         )
      AND    a.a2 is something;
      

      【讨论】:

        【解决方案4】:

        结果并不奇怪,子查询可以引用PARENT查询。这称为相关子查询,非常常见。在您的示例中,您使用了 IN 运算符,但通常使用 IN 操作优化查询是使用相关子查询将 IN 替换为 EXISTS 运算符。

        详细说明 Erwin 关于 EXISTS 更快的评论,这是因为当您使用 IN 时,“有时”需要查询来查找集合的所有值。而使用 EXISTS 只需要找到满足条件的第一次出现。然而,查询计划优化两者可能相同的情况。但是使用 EXISTS 可以显式帮助优化器更快地构建预期的查询计划。

        【讨论】:

          猜你喜欢
          • 2016-12-19
          • 1970-01-01
          • 2015-06-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-09-17
          相关资源
          最近更新 更多