【问题标题】:Query where two columns are in the result of nested query查询嵌套查询结果中的两列
【发布时间】:2011-01-26 01:19:44
【问题描述】:

我正在编写这样的查询:

select * from myTable where X in (select X from Y) and XX in (select X from Y)

X 和 XX 列的值必须在同一查询的结果中:select X from Y。

我认为这个查询被调用了两次,所以它毫无意义。还有其他选择我可以更有效地编写此查询吗?也许是临时表?

【问题讨论】:

    标签: sql sql-server tsql sql-server-2008 query-optimization


    【解决方案1】:

    实际上没有,鉴于 myTable.X 和 myTable.YY 匹配的 X 可能不在同一行,因此没有更聪明的方法来编写此代码(无需访问 Y 两次)。

    作为替代,查询的 EXISTS 形式是

    select *
    from myTable A
    where exists (select * from Y where A.X = Y.X)
      and exists (select * from Y where A.XX = Y.X)
    

    如果 Y 包含 X 值 1,2,3,4,5、x.x = 2 和 x.xx = 4,则它们都存在(在 Y 中的不同记录上)并且 myTable 中的记录应显示在输出中。

    编辑:这个答案之前说You could rewrite this using _EXISTS_ clauses which will work faster than _IN_。正如 Martin 所指出的,这不是真的(对于 SQL Server 2005 及更高版本当然不是)。查看链接

    【讨论】:

    • +1,但存在不一定会更快 - 我的简单测试为两种方法提供了完全相同的查询计划。查询优化器非常聪明,在“优化”你的 sql 时检查计划总是值得的。
    • +1 在同意之前我不得不离线尝试其他几个,但这是最有效的......
    • 每个人都有过糟糕的日子,不要把它放在心上:-)。我会赞成中和这一点,因为您的回答显然是正确的(尽管我的回答对原始问题更好:-)
    • 在我的错误中,我投了反对票。亚历克斯礼貌地提醒我我的理解是不正确的——你称我为白痴(直接引用,在我威胁要提醒模组后你已经编辑了评论),并告诉我立即删除它。现在你觉得有必要贴上我的名字来表明误解,并要求扭转局面。是的,伙计,我会马上解决的。当我们这样做的时候,reversing the downvote on my answer earlier 呢?如果你不喜欢它,不要对别人这样做。
    • in 和 exists 做get treated the same in SQL Server AFAIK。
    【解决方案2】:

    尝试通过仅引用一次Y 来编写此查询可能不会特别有效。但是,鉴于您使用的是 SQL Server 2008,因此可以使用多种变体:

    Select ...
    From MyTable As T
    Where Exists    (
                    Select 1
                    From Y
                    Where Y.X = T.X
                    Intersect
                    Select 1
                    From Y
                    Where Y.X = T.XX
                    )
    

    加法

    实际上,我可以想到一种方法,无需多次使用Y 就可以做到这一点(没有提到多次使用MyTable)。然而,这更多是出于学术原因,因为我认为使用我的第一个解决方案可能会表现得更好:

    Select ...
    From MyTable As T
    Where Exists    (
                    Select 1
                    From Y
                    Where Exists( 
                                Select 1 
                                From MyTable1 As T1 
                                Where T1.X = Y.X 
                                Intersect
                                Select 1 
                                From MyTable1 As T2 
                                Where T2.XX = Y.X
                                )
                        And Y.X In(T.X, T.XX)
                    )
    

    【讨论】:

      【解决方案3】:
      WITH
         w_tmp AS(
            SELECT x
              FROM y
         )
      SELECT * 
        FROM myTable
       WHERE x IN (SELECT x FROM w_tmp)
         AND xx IN (SELECT x FROM w_tmp)
      

      (我已在 Oracle 文档中阅读过此内容,但我认为 MS 也可以进行此优化)
      这样优化器可以确定您正在执行相同的查询,并且可以创建临时表来兑现结果(但仍然由优化器决定是否值得。对于微小的查询,创建临时表的开销可能太高)。

      另外(实际上这对我来说更重要),当子查询是 50 行时,human 更容易看到,在两种情况下都使用相同的东西。很像将长函数分解为子例程

      Docs on MSDN

      【讨论】:

      • 这可能会让您感到惊讶,但 CTE w_tmp 是 evaluated twice,是的。检查查询计划。它没有存储在一些临时内存中。
      • @cyberkiwi:由优化器决定是否值得。对于微小的查询,创建临时表的开销可能太高。感谢您的评论,我会更新帖子以澄清这一点。
      • Oracle 有实现 CTE 的提示(Oracle 称之为“子查询分解”),但除此之外,使用 CTE 并没有性能优势。
      • @OMG Ponies:它可以在没有提示的情况下做到这一点。请参阅 asktom.oracle.com/pls/asktom/… 的 Tom Kyte 的第二个答案。或者这篇文章中的例子oracle-developer.net/display.php?id=212
      【解决方案4】:

      不确定问题是什么,但不是简单的 JOIN 答案吗?

      SELECT t.* 
      FROM myTable 
      JOIN Y y1 ON y1.X = myTable.X
      JOIN Y y2 ON y2.X = myTable.XX
      

      或

      SELECT t.*
      FROM myTable, Y y1, Y y2
      WHERE y1.X = myTable.X AND y2.X = myTable.XX
      

      添加:如果强烈需要消除对 Y 的第二次查询,让我们颠倒逻辑:

      ;WITH A(X)
      AS (
        -- this will select all values that can be found in Y and myTable X and XX fields.
        SELECT Y.X -- if there are a lot of dups, add DISTINCT
        FROM Y, myTable
        WHERE Y.X IN (myTable.X, myTableXX)
      )
      -- now join back to the orignal table and filter.
      SELECT t.* 
      FROM myTable
      -- similar to what has been mentioned before 
      WHERE EXISTS(SELECT TOP 1 * from A where A.X = myTable.X)
            AND EXISTS(SELECT TOP 1 * from A where A.X = myTable.XX)
      

      如果你不喜欢 WITH,你可以使用 SELECT INTO 子句创建内存表。

      【讨论】:

      • 问题是:完全相同的查询select X from Y被执行了两次
      • 您不信任 SQL Server 缓存和优化策略?
      • 添加了另一种方式。奇怪的一个。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-26
      相关资源
      最近更新 更多