有两个表。 Customers
,其中custid
作为PK,Orders
,其中custid
作为FK。
Customers
表具有列custid
,companyname
Orders
表具有列custid
,orderid
,orderdate
我想退回在2007年而不是'2008'年订购的客户。我要在最终结果中返回custid
和companyname
。
我有query1,它通过总共7个不同的custid
获取正确的结果
我有query2,可以让我获得更多不同的行,即最终结果中有86行
query1
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)
query2
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的问题以及为什么它无法给出正确的结果?
答案 0 :(得分:2)
正确的查询是第一个。
如user2722968所述,使用:
WHERE YEAR(orderdate) = '2007' AND YEAR(orderdate) <> '2008'
每行工作。因此,如果一个监护人在2007年和2008年都同时拥有一个orderid,
前面提到的WHERE
确实会返回2007行,就像确实有YEAR(orderdate) = '2007' AND YEAR(orderdate) <> '2008'
一样。
相反,(NOT) EXISTS
中的不同代码不是对行而是对结果集执行操作(semijoi)。这就是您所需要的。
一个建议:如果可以避免使用函数,这是一个很好的性能习惯,因为当您将函数应用于字段时,如果有一个索引,则不能使用它来加快计算速度。因此,最好使用YEAR(orderdate)= 2007,而不是:
orderdate>='20070101' and orderdate<'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)
答案 1 :(得分:1)
这两个带有“ EXISTS”的查询为什么表现不同?
因为:
看起来第一个查询更有意义。
答案 2 :(得分:1)
好吧,让我们有一个拥有一个客户(客户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) <> 2008
的行(第一行)。但是,这并不意味着与2008年不存在不同的行!
很显然,第一个查询不返回任何结果,因为客户1不满足not exists
谓词。如果我们用关系代数表达第一个查询,那么它对应于两个集合之间的差,但是,第二个查询只是带有条件的普通联接。
答案 3 :(得分:1)
有一种更简单的方法可以使用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'