【问题标题】:T-SQL: How to Select Values in Value List that are NOT IN the Table?T-SQL:如何在值列表中选择不在表中的值?
【发布时间】:2012-02-02 13:32:11
【问题描述】:

我有一个电子邮件地址列表,其中一些在我的表格中,而另一些不在。我想从该列表中选择所有电子邮件以及它们是否在表中。

我可以像这样获取邮件地址在表格中的用户:
SELECT u.* FROM USERS u WHERE u.EMAIL IN ('email1', 'email2', 'email3')

但是如何在该列表中选择表中不存在的值?

此外,我该如何选择:

E-Mail | Status
email1 | Exist  
email2 | Exist  
email3 | Not Exist  
email4 | Exist  

提前致谢。

【问题讨论】:

  • 你的意思是微软的 SQL Server 的 T-SQL 吗?如果有,是哪个版本?
  • 是的,MS SQL Server。我正在使用 2005 和 2008 R2。
  • 好的,在这种情况下,我推荐 Martin Smith 的答案。我问是因为即使 SQL Server 2000 仍然使用相当多,他的回答也行不通。
  • 通过他的编辑,他的回答甚至适用于所有版本:)
  • 谢谢你hvd,如果你不问,马丁可能不会编辑。我不知道 SQL 正在为远程服务器运行命令,我的 Studio 是 2008,但远程服务器是 2005,所以 (VALUES) 不起作用但 (UNION ALL) 起作用。

标签: sql sql-server tsql


【解决方案1】:

对于 SQL Server 2008

SELECT email,
       CASE
         WHEN EXISTS(SELECT *
                     FROM   Users U
                     WHERE  E.email = U.email) THEN 'Exist'
         ELSE 'Not Exist'
       END AS [Status]
FROM   (VALUES('email1'),
              ('email2'),
              ('email3'),
              ('email4')) E(email)  

对于以前的版本,您可以使用派生表UNION ALL-ing 常量执行类似的操作。

/*The SELECT list is the same as previously*/
FROM (
SELECT 'email1' UNION ALL
SELECT 'email2' UNION ALL
SELECT 'email3' UNION ALL
SELECT 'email4'
)  E(email)

或者,如果您只想要不存在的(如标题所示)而不是问题中给出的确切结果集,您可以简单地这样做

SELECT email
FROM   (VALUES('email1'),
              ('email2'),
              ('email3'),
              ('email4')) E(email)  
EXCEPT
SELECT email
FROM Users

【讨论】:

    【解决方案2】:

    您需要以某种方式使用这些值创建一个表,然后使用NOT IN。这可以通过临时表、CTE(通用表表达式)或Table Values Constructor(在 SQL-Server 2008 中可用)来完成:

    SELECT email
    FROM
        ( VALUES 
            ('email1')
          , ('email2')
          , ('email3')
        ) AS Checking (email)
    WHERE email NOT IN 
          ( SELECT email 
            FROM Users
          ) 
    

    可以通过LEFT JOINEXISTS 子查询找到第二个结果:

    SELECT email
         , CASE WHEN EXISTS ( SELECT * 
                              FROM Users u
                              WHERE u.email = Checking.email
                            ) 
                THEN 'Exists'
                ELSE 'Not exists'
           END AS status 
    FROM
        ( VALUES 
            ('email1')
          , ('email2')
          , ('email3')
        ) AS Checking (email)
    

    【讨论】:

    • 这给出了表中不在列表中的值。问题反过来问:列表中的那些不在表格中。
    • 通过编辑,如果Users.email 可以为空(我们不知道是否为空)仍然是不正确的
    • @hvd:你说得对,我们不知道它是否可以为空。不过,查询会正确回答。
    • 你的第二个查询会,你的第一个不会。 WHERE 'a' IN ('b', NULL, 'c')UNKNOWN 而不是 FALSE,而 NOT UNKNOWN 仍然是 UNKNOWN。换句话说,如果任何用户的电子邮件地址是NULL,第一个查询将不会返回任何内容。
    • @hvd:我知道,但这可能是 OP 的“正确”行为 :) 为什么他的列中还有 Nulls?
    【解决方案3】:

    您应该有一个包含要检查的电子邮件列表的表格。然后做这个查询:

    SELECT E.Email, CASE WHEN U.Email IS NULL THEN 'Not Exists' ELSE 'Exists' END Status
    FROM EmailsToCheck E
    LEFT JOIN (SELECT DISTINCT Email FROM Users) U
    ON E.Email = U.Email
    

    【讨论】:

    • 谢谢,我不想走太远。马丁的回答做到了。
    【解决方案4】:

    如果您不想在列表中包含数据库中的电子邮件,您可以执行以下操作:

    select    u.name
            , u.EMAIL
            , a.emailadres
            , case when a.emailadres is null then 'Not exists'
                   else 'Exists'
              end as 'Existence'
    from      users u
              left join (          select 'email1' as emailadres
                         union all select 'email2'
                         union all select 'email3') a
                on  a.emailadres = u.EMAIL)
    

    这样你会得到类似的结果

    name | email  | emailadres | existence
    -----|--------|------------|----------
    NULL | NULL   | a@b.com    | Not exists
    Jan  | j@j.nl | j@j.nl     | Exists
    

    在这种情况下,使用 IN 或 EXISTS 运算符比左连接更繁重。

    祝你好运:)

    【讨论】:

    • 谢谢,您的回答虽然迟了,但也正确。很高兴我不必为值创建表格。
    • EXISTS 会比外连接更有效,因为它可以短路。如果Users 中有重复地址,您可以多次返回同一个地址。有关如何处理 EXISTS 的一些详细信息,请参阅 Subqueries in CASE Expressions
    【解决方案5】:

    这应该适用于所有 SQL 版本。

    SELECT  E.AccessCode ,
            CASE WHEN C.AccessCode IS NOT NULL THEN 'Exist'
                 ELSE 'Not Exist'
            END AS [Status]
    FROM    ( SELECT    '60552' AS AccessCode
              UNION ALL
              SELECT    '80630'
              UNION ALL
              SELECT    '1611'
              UNION ALL
              SELECT    '0000'
            ) AS E
            LEFT OUTER JOIN dbo.Credentials C ON E.AccessCode = c.AccessCode
    

    【讨论】:

      【解决方案6】:

      使用这个: -- SQL Server 2008 或更高版本

      SELECT U.* 
      FROM USERS AS U
      Inner Join (
        SELECT   
          EMail, [Status]
        FROM
          (
            Values
              ('email1', 'Exist'),
              ('email2', 'Exist'),
              ('email3', 'Not Exist'),
              ('email4', 'Exist')
          )AS TempTableName (EMail, [Status])
        Where TempTableName.EMail IN ('email1','email2','email3')
      ) As TMP ON U.EMail = TMP.EMail
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-11
        • 2023-01-26
        • 1970-01-01
        • 2019-04-11
        • 1970-01-01
        相关资源
        最近更新 更多