如何使用T-SQL Exists关键字?

时间:2009-05-26 20:17:21

标签: sql sql-server tsql

我有一个查询我想作为子查询运行,它将返回一组FK。有了它们,我想只返回具有匹配键的行。

子查询:

SELECT ID 
FROM tblTenantTransCode 
WHERE
    tblTenantTransCode.CheckbookCode = 
      (SELECT ID FROM tblCheckbookCode WHERE Description = 'Rent Income')

这将返回所有具有与租金收入匹配的支票簿代码的交易代码

现在我想选择所有事务,其事务代码与子查询中返回的ID匹配。我已经做到这一点,但SQL Server抱怨语法错误。我怎么能这样做?

完整查询:

SELECT * 
FROM tblTransaction
WHERE
    tblTransaction.TransactionCode IN 
      (SELECT ID FROM tblTenantTransCode 
       WHERE tblTenantTransCode.CheckbookCode = 
           (SELECT ID FROM tblCheckbookCode WHERE Description = 'Rent Income'))

表:

tblCheckbookCode  
   ID  
   Description  
   Other Info  

tblTenantTransCode  
   ID  
   CheckbookCode <-- fk we're looking for   
                     in the tblCheckbookCode.   
                     We're selecting only checkbook codes   
                     that have the Description 'Rent Income'  
   Other Info  

tblTransactions  
   ID  
   TransactionCode <-- fk to tenant transaction code.   
                       We're looking for an ID that is returned   
                       in the above query/join  

5 个答案:

答案 0 :(得分:64)

要回答有关使用 EXISTS 关键字的问题,以下是根据您问题中当前给出的查询使用EXISTS谓词的示例查询。

    SELECT t.*
      FROM tblTransaction t
     WHERE EXISTS
           ( 
             SELECT 1
               FROM tblTenantTransCode ttc
               JOIN tblCheckbookCode cc
                 ON (cc.ID = ttc.CheckbookCode AND cc.Description='Rent Income')
              WHERE ttc.ID = t.TransactionCode
           )

其他详细信息:

我们都认识到有各种SQL语句会返回满足指定要求的结果集。这些查询的观察性能可能会有所不同。性能尤其取决于DBMS,优化器模式,查询计划和统计信息(行数和数据值分布)。

EXISTS的一个优点是它清楚地表明我们对从子查询中的表中返回任何表达式不感兴趣。它用于在逻辑上将子查询与外部查询分开,方式是JOIN没有。

使用EXISTS的另一个好处是,如果我们改为使用JOIN,则可以避免返回可能(可能)返回的重复行。

EXISTS谓词可用于测试子表中是否存在任何相关行,而无需连接。例如,以下查询返回一组至少包含一个关联line_item的所有订单:

    SELECT o.*
      FROM order o
     WHERE EXISTS
           ( SELECT 1
               FROM line_item li
              WHERE li.order_id = o.id
           )

请注意,子查询不需要查找所有匹配的行项,只需找到一行即可满足条件。 (如果我们将此查询编写为JOIN,那么只要订单包含多个订单项,我们就会返回重复的行。)

NOT EXISTS谓词也很有用,例如,返回一组具有任何关联line_items的订单。

    SELECT o.*
      FROM order o
     WHERE NOT EXISTS
           ( SELECT 1
               FROM line_item li
              WHERE li.order_id = o.id
           )

当然,NOT EXISTS只是一种选择。使用OUTER join和IS NULL测试可以获得等效的结果集(假设我们在line_item表中至少有一个表达式是NOT NULL)

    SELECT o.*
      FROM order o
      LEFT
      JOIN line_item li ON (li.order_id = o.id)
     WHERE li.id IS NULL

似乎有很多关于需要使用IN谓词或需要使用JOIN的讨论(与原始问题的答案有关)。

这些结构是替代品,但不是必需的。查询可以返回所需的结果集,而无需使用IN且不使用JOIN。可以使用使用EXISTS谓词的查询返回结果集。 (请注意,OP问题的标题确实询问了如何使用EXISTS关键字。)

这是另一个替代查询(这不是我的第一选择),但返回的结果集确实满足指定的要求:


    SELECT t.*
      FROM tblTransaction t
     WHERE EXISTS
           ( 
             SELECT 1
               FROM tblTenantTransCode ttc
              WHERE ttc.ID = t.TransactionCode
                AND EXISTS 
                    (
                      SELECT 1 
                        FROM tblCheckbookCode cc
                       WHERE cc.ID = ttc.CheckbookCode
                         AND cc.Description = 'Rent Income'
                    )
           )

最重要的是,在给定所有可能的条件集的情况下,查询应返回正确的结果集,满足指定的要求。

这里作为答案提出的一些查询 NOT 返回请求的结果集,或者如果他们这样做,他们偶然会这样做。如果我们预先假设有关数据的内容,则某些查询会有效,因此某些列为UNIQUENOT NULL

效果差异

有时,使用EXISTS谓词的查询的效果不如使用JOININ谓词的查询效果好。在某些情况下,它可能表现更好。 (对于EXISTS谓词,子查询只需查找满足条件的一行,而不是查找所有匹配的行,如JOIN所要求的那样。)

通过观察可以最好地衡量各种查询选项的性能。

答案 1 :(得分:5)

您正在描述内部联接。

select tc.id 
from tblTenantTransCode tc 
   inner join tblCheckbookCode cc on tc.CheckbookCode = cc.CheckbookCode

编辑:它仍然是一个内部联接。我认为还没有任何理由使用IN子句。

select *
from tblTransaction t
   inner join tblTenantTransCode tc on tc.id = t.TransactionCode
   inner join tblCheckbookCode cc on cc.id = tc.CheckbookCode
where cc.description = 'Rent Income'

编辑:如果必须使用EXISTS谓词来解决此问题,请参阅@ spencer7953的答案。然而,从我所看到的,上面的解决方案更简单,并且基于“子查询”为您工作的事实存在唯一性的假设(如果该表中没有唯一性,则不会100%的时间)。我也在致辞

  

现在我要选择所有交易   其中他们的交易代码   匹配子查询中返回的ID

在我的回答中。如果请求是这样的话:

  

现在我要选择All Transcations   当任何事务代码与子查询中返回的ID匹配时。

我会使用EXISTS查看子表中是否存在任何事务代码,并根据需要返回每一行或没有。

答案 2 :(得分:1)

鉴于您的完整查询,此查询将为您提供使用单个联接所需的位置。

联接过滤掉任何没有“租金收入”交易代码的交易。它将从第一个表中获取所有记录,构建第二个表的子集(WHERE子句限制记录),然后过滤第一个表,其中这些表数学连接条件。

SELECT 
    t.* 
FROM 
    tblTransaction t
    INNER JOIN tblTenantTransCode c ON
        t.TransactionCode = c.ID
    INNER JOIN tblCheckbookCode chk ON
        c.CheckbookCode = chk.ID
WHERE
    chk.Description = 'Rent Income'

编辑:另一个注意事项:避免使用SELECT * - 始终指定列。 编辑Dos:我错过了有三张桌子。更正!谢谢,斯宾塞!

答案 3 :(得分:0)

试试这个:

SELECT
    tblTenantTransCode.ID 
    FROM tblCheckbookCode 
        INNER JOIN tblTenantTransCode ON tblCheckbookCode.ID=tblTenantTransCode.CheckbookCode
    WHERE tblCheckbookCode.Description = 'Rent Income'

确保您为tblCheckbookCode.Description编制索引。

答案 4 :(得分:0)

您需要使用'IN'子句:

select id from tblTenantTransCode
where tblTenantTransCode.CheckbookCode in
    (select id from tblCheckbookCode
     where description = 'rent income')

虽然内部联接可能是更好的解决方案......

select ttc.id from tblTenantTransCode as ttc
inner join tblCheckbookCode as tcc
    on ttc.CheckBookId = tcc.id
where tcc.description = 'rent income'