最好用一个例子来解释:)
假设我有一张桌子
CREATE TABLE dbo.Customer (
CustomerId INT PRIMARY KEY,
Name NVARCHAR(50)
)
CREATE TABLE dbo.ShoppingBasket (
ShoppingBasketId INT PRIMARY KEY,
CustomerId INT NOT NULL FOREIGN KEY dbo.Customer(CustomerId),
ItemName NVARCHAR(50)
)
示例数据
INSERT INTO dbo.Customer
VALUES (1, 'Steve'), (2, 'Bucky')
INSERT INTO dbo.ShoppingBasket
VALUES (1, 1, 'Banana'), (2, 1, 'Orange'), (3, 2, 'Orange')
现在,我想找到所有客户,他们的购物篮中都有香蕉和橙子。所以在上面的情况下,它应该只返回史蒂夫。因为Bucky只有香蕉。
以下查询适用于此
SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName IN ('Banana', 'Orange')
GROUP BY CustomerId
HAVING COUNT(CustomerId) = 2
)
没关系。现在,如果我想要所有只有Orange的客户,则上述查询自
后失败SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName = 'Orange'
GROUP BY CustomerId
HAVING COUNT(CustomerId) = 1
)
过滤掉购物篮,然后应用group and having子句。因此史蒂夫和巴基都回来了,而只有巴基应该归还。
有人能指出我正确的方向找到这样的查询,我想我总是可以在存在的子查询中做另一个NOT EXIST
,以确保找不到其他项目。 E.g。
SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName = 'Orange'
AND NOT EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b2
WHERE b2.CustomerId = b.CustomerId
AND b.ItemName <> 'Orange'
)
)
但是,如果有一种更优雅的方式来处理它,那就是徘徊。最好不要在同一张桌子上进行额外的,否定的连接。
答案 0 :(得分:1)
你应该检查不同的ItemName而不是customerId,例如:
select c.*
from dbo.Customer
inner join(
select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
where ItemName IN ('Banana', 'Orange')
group by CustomerId
having count_name = 2
) t on t.CustomerId = c.CustomerId
如果你需要两次检查项目名称的数量,你可以用两部分组成内部联接
select c.*
from dbo.Customer
inner join(
select CustomerId
from ShoppingBasket b
where ItemName IN ('Banana', 'Orange')
INNER JOIN (
Select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
group by CustomerId
having count_name = 2
) t2 ON t2.CustomerId = b.CustomerId
) t on t.CustomerId = c.CustomerId
和Orange ..
select c.*
from dbo.Customer
inner join(
select CustomerId
from ShoppingBasket b
where ItemName IN ('Orange')
INNER JOIN (
Select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
group by CustomerId
having count_name = 1
) t2 ON t2.CustomerId = b.CustomerId
) t on t.CustomerId = c.CustomerId
问题在于ambiguos中的in子句,因为对于ShoppingBasket CustomerId也返回true,并且有一个正面检查 那么你应该处理的是一个in子句(相当于OR),并为所有客户提供条款,这些客户在你正在寻找的那个号码上有许多不同的名字等价
Select CustomerId
from ShoppingBasket a
inner join ShoppingBasket b a.ItemName = 'Orange' and b.ItemName = 'Banana'
and customerId IN (
Select CustomerId
from ShoppingBasket
group by CustomerId
having count(distinct ItemName) = 2
)
答案 1 :(得分:0)
我喜欢使用group by
和having
执行此操作。如果你想要“香蕉”和“橙色”:
select sb.customerId
from dbo.ShoppingBasket sb
where sb.itemName in ('banana', 'orange')
group by sb.customerId
having count(distinct itemName) = 2; -- has both
如果你想要这两个项目,那么请使用这个更通用的形式:
select sb.customerId
from dbo.ShoppingBasket sb
where sb.itemName in ('banana', 'orange')
group by sb.customerId
having sum(case when sb.itemName = 'banana' then 1 else 0 end) > 0 and
sum(case when sb.itemName = 'orange' then 1 else 0 end) > 0 and
sum(case when sb.itemName not in ('orange, 'banana') then 1 else 0 end) = 0 ;
您可以轻松扩展此版本。每个项目都有自己的sum()
。您还可以包含多个项目,例如“香蕉和(橙色或克莱门汀)”。