【问题标题】:Why do these two queries with 'EXISTS' behave differently?为什么这两个带有“EXISTS”的查询表现不同?
【发布时间】:2019-09-24 18:00:33
【问题描述】:

有两张桌子。 Customerscustid 作为 PK 和 Orderscustid 作为 FK。

Customers 表有列custidcompanyname

Orders 表有列custidorderidorderdate

我想退回在 2007 年而不是在 '2008' 订购的客户。我想在最终结果中返回custidcompanyname

我有 query1 可以获取正确的结果,总共有 7 个不同的 custid 我有 query2,它给了我更多不同的行,即最终结果中有 86 行

查询1

SELECT custid, companyname
FROM customers c
WHER EXISTS
 (SELECT custid
  FROM orders o
  WHERE YEAR(orderdate) = '2007'AND o.custid = c.custid)
AND NOT EXISTS
 (SELECT custid
  FROM orders o
  WHERE YEAR(orderdate) = '2008'AND o.custid=c.custid)

查询2

SELECT DISTINCT custid, companyname
FROM customers c
WHERE EXISTS
 (SELECT custid
  FROM orders o
  WHERE YEAR(orderdate) = '2007'
        AND YEAR(orderdate) <> '2008'
        AND o.custid=c.custid)

我不明白 query2 的问题以及为什么它不能给出正确的结果?

【问题讨论】:

  • 能否请您提供两个表中的一些示例数据以供我们使用?
  • code2 中的子查询将有效地查询 2007 年的所有客户。请记住,条件是按行计算的:如果第一个条件 (= 2017) 成立,则第二个条件 (&lt;&gt; 2018) 成立根据定义。
  • 当您说YEAR(orderdate) = '2007' 时,它永远不会是其他任何东西(条件是每行),除非您引入OR 条件.具体值不能同时为 2007 和其他值。

标签: sql sql-server tsql sql-server-2012 exists


【解决方案1】:

正确的查询是第一个

正如 user2722968 所说,使用: WHERE YEAR(orderdate) = '2007' AND YEAR(orderdate) &lt;&gt; '2008' 每行工作。所以,如果一个 custid 在 2007 年和 2008 年都有一个 orderid, 前面提到的WHERE 确实会返回 2007 行,因为它确实有 YEAR(orderdate) = '2007' AND YEAR(orderdate) &lt;&gt; '2008'

相反,(NOT) EXISTS 中的不同代码不是对行而是对结果集执行操作 (semijoi)。这就是你需要的。

建议:如果可以避免使用函数,最好不要使用函数,因为当你将函数应用到字段时,如果上面有索引,它不能用于加速计算。因此,最好不要使用 YEAR(orderdate)=2007:

orderdate&gt;='20070101' and orderdate&lt;'20080101'

考虑到这一点,查询变为:

SELECT custid, companyname
FROM customers c
WHERE EXISTS
 (SELECT custid
  FROM orders o
  WHERE orderdate>='20070101' and orderdate<'20080101' AND o.custid = c.custid)
AND NOT EXISTS
 (SELECT custid
  FROM orders o
  WHERE orderdate>='20080101' and orderdate<'20090101' AND o.custid=c.custid)

【讨论】:

【解决方案2】:

好的,让我们有一个客户(客户 1)的 customers 表和以下 orders

custid   orderid  orderdate
---------------------------
  1         1       1.1.2007
  1         2       1.1.2008       

您的第二个查询为客户解释子查询

SELECT custid
FROM orders o
WHERE YEAR(orderdate) = 2007 AND YEAR(orderdate) <> 2008 AND o.custid = 1

它返回第一行。因此,exists 被评估为客户的true,因为有一行year(orderdate) = 2007 and year(orderdate) &lt;&gt; 2008(第一行)。但是,这并不意味着与 2008 年不存在不同的行!

显然,第一个查询没有返回结果,因为客户 1 不满足 not exists 谓词。如果我们用关系代数表示第一个查询,那么它对应于两个集合之间的差异,但是,第二个查询只是一个带有条件的普通连接。

【讨论】:

    【解决方案3】:

    为什么这两个带有“EXISTS”的查询表现不同?

    因为:

    • 如果有 2007 年的订单并且在 2008 年没有(不同的)订单,第一个查询将返回一个客户。
    • 但是第二个查询会返回一个客户,如果有2007年的订单,但同样的订单不在2008年(由于订单已经在2007年,所以不在2008年,所以条件&lt;&gt; 2008是冗余)。

    看起来第一个查询更有意义。

    【讨论】:

      【解决方案4】:

      EXCEPT 有一种更简单的方法可以解决这个问题

      select c.custid, c.companyname
      from Customers c
      join
      Orders o
      on o.custid=c.custid
      where orderdate>='20070101' and orderdate<'20080101'
      except
      select c.custid, c.companyname
      from [TSQL2012].Sales.Customers c
      join
      [TSQL2012].Sales.Orders o
      on o.custid=c.custid
      where orderdate>='20080101' and orderdate<'20090101'
      

      【讨论】:

        猜你喜欢
        • 2011-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-18
        • 1970-01-01
        • 2013-12-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多