【问题标题】:Filtering Query with NOT EXISTS过滤不存在​​的查询
【发布时间】:2014-11-04 11:10:10
【问题描述】:

我目前正在尝试使用 Visual Studio 中的 SQL Server 查询数据库。有问题的数据库包含支付信息,主要通过 OrderID 和 License ID 识别交易及其产生的软件许可证。有时,这些许可证会因滥用而被吊销。

现在,我正在尝试运行一个基于此返回所有客户的查询:

Select 
   [Order].LastName,
   [Order].FirstName,
   [Order].CompanyOrganization,
   [Order].EmailAddress,
   [Order].Country,
   [License].LicenseID,
   [License].InstanceCount

From [Order], [License]

Where
    [License].OrderID = [Order].OrderID
    AND [Order].Status = 1
    AND not exists (Select LicenseID From [LicenseRevocation])

Order by [License].InstanceCount DESC;

查询没有返回任何结果,我知道这是因为“不存在”部分。但是,我不确定为什么。有人可以弄清楚“EXISTS”是如何工作的以及如何在我的查询中实现它吗?

【问题讨论】:

    标签: sql-server tsql not-exists


    【解决方案1】:

    exists() 函数在其内部的查询将产生至少一条记录时为真,因此not exists() 仅在其内部的查询将产生零条记录时为真。

    在您的情况下,您正在查询 not exists() 内的整个 LicenseRevocation,因此只有当该表完全为空时,您的查询才会返回任何内容。

    您可以在查询中使用条件来查找其中的特定记录,如下所示:

    not exists (Select * From [LicenseRevocation] Where LicenseID = [License].LicenceID)
    

    这将使查询返回LicenseRevocation表中没有对应记录的记录。

    【讨论】:

      【解决方案2】:

      你需要定义一个条件来检查值是否存在,

      还可以为您的联接使用ON 子句语法。

      Select [Order].LastName
           , [Order].FirstName
           , [Order].CompanyOrganization
           , [Order].EmailAddress
           , [Order].Country
           , [License].LicenseID
           , [License].InstanceCount
      
      From [Order] 
      INNER JOIN [License] ON [License].OrderID = [Order].OrderID 
      
      Where [Order].[Status] = 1 
      AND NOT EXISTS (Select 1 
                      From [LicenseRevocation]
                      WHERE LicenseID = [License].LicenseID)  --<-- you are missing this condition
      Order by [License].InstanceCount DESC;
      

      【讨论】:

      • 有效!你能解释一下为什么我想Select 1 而不是特定的列吗?这是我唯一不明白的部分。
      • 从逻辑上讲,如果它根据 WHERE 子句中定义的条件找到一行,则它选择 1 (True),否则它返回 NULL (False),这在逻辑上没有区别。当您只需要truefalse 时,我看不出尝试返回列值的意义,这可能比简单地选择 1 更昂贵。
      • 非常有意义。谢谢!
      【解决方案3】:

      此解决方案使用 NOT IN 和正确的 JOIN 语法,但应该会给您想要的结果

      SELECT  [Order].LastName, [Order].FirstName, [Order].CompanyOrganization, 
              [Order].EmailAddress, [Order].Country,
              [License].LicenseID, [License].InstanceCount
      FROM    [Order] 
      INNER JOIN [License] ON [License].OrderID = [Order].OrderID 
      WHERE  [Order].Status = 1 
      AND  [License].LicenseID NOT IN  (
           SELECT  LicenseID
           FROM    [LicenseRevocation] )
      ORDER BY [License].InstanceCount DESC;
      

      ps。使用表ALIAS

      【讨论】:

      • 从技术上讲,来自 OP 的连接语法是正确的。然而,它是较旧的 ANSI-89 样式连接,而您的连接是较新的 ANSI-92 样式连接。 92 样式连接更易于阅读且不易出错。
      • @SeanLange 谢谢肖恩,这就是我的意思。更常见且更易于阅读
      • 建议使用别名 +1000。可能会添加到不使用保留字作为对象名称,因为使用起来很痛苦。
      【解决方案4】:

      首先,使用正确的 JOINS 编写查询,并使用标准的 LEFT OUTER JOIN:

      Select 
         [Order].LastName,
         [Order].FirstName,
         [Order].CompanyOrganization,
         [Order].EmailAddress,
         [Order].Country,
         [License].LicenseID,
         [License].InstanceCount
      
      From [Order]
      INNER JOIN [License] ON [License].OrderID = [Order].OrderID
      LEFT OUTER JOIN [LicenseRevocation] ON [License].LicenseID = [LicenseRevocation].LicenseID
      Where [Order].Status = 1
      AND [LicenseRevocation].LicenseID IS NULL
      
      Order by [License].InstanceCount DESC;
      

      【讨论】:

      • 从技术上讲,来自 OP 的连接语法是正确的。然而,它是较旧的 ANSI-89 样式连接,而您的连接是较新的 ANSI-92 样式连接。 92 样式连接更易于阅读且不易出错。此外,您的查询也不完全相同。如果在 LicenseRevocation 中匹配的行超过 1 行,您将返回更多行。
      • 感谢您在 ANSI-89 与 ANSI-92 上的 cmets。关于 LEFT JOIN 到 LicenseRevocation,我同意,除了我们过滤掉任何匹配的记录,因此在这种情况下最终结果可以正常工作。如果我们没有“WHERE [LicenseRevocation].LicenseID IS NULL”,那么我完全同意你的看法。
      猜你喜欢
      • 1970-01-01
      • 2016-05-03
      • 1970-01-01
      • 1970-01-01
      • 2022-11-04
      • 2017-10-12
      • 2023-03-30
      • 2018-01-01
      • 2021-04-02
      相关资源
      最近更新 更多