SQL 子查询替代 INTERSECT
Posted
技术标签:
【中文标题】SQL 子查询替代 INTERSECT【英文标题】:SQL subqueries alternative to INTERSECT 【发布时间】:2012-01-23 16:26:21 【问题描述】:我有两张桌子:
P
和 PC
(主/从列 Id 加入)
Table P:
Id integer
Name varchar(12)
Table PC:
Id integer
Code varchar(12)
Val number
我想从 P 中获取满足以下同时条件的所有名称:
拥有一台带有PC.Code='A'
和Val>100
的PC
拥有另一台带有PC.Code='B'
和Val>80
的PC
总之,我只对细节符合这两个条件的 P.Name 感兴趣。有没有不使用 INTERSECT 的方法来选择?
INTERSECT 查询是:
Select P.Name
from P, PC
where P.Id=PC.Id
and PC.Code='A' and Val>100
INTERSECT
Select P.Name
from P, PC
where P.Id=PC.Id
and PC.Code='B' and Val>80
(兴趣是检查性能并允许在 Access 中运行查询)
【问题讨论】:
This article 向您展示了如何使用 JOIN 重写 INTERSECT 查询。具体来说,请参阅 Vinko Vrsalovic 的解决方案。 【参考方案1】:不知道性能怎么样..试试..
SELECT P.Name
FROM P
INNER JOIN PC AS a ON P.Id=a.Id and a.Cod='A' and a.Val>100
INNER JOIN PC AS b ON P.Id=b.Id and a.Cod='B' and a.Val>80
【讨论】:
【参考方案2】:这是一种关系等效的替代方法(即消除重复行):
SELECT P.Name
FROM P
WHERE EXISTS (
SELECT *
FROM PC
WHERE P.Id = PC.Id
AND PC.Code ='A'
AND PC.Val > 100
)
AND EXISTS (
SELECT *
FROM PC
WHERE P.Id = PC.Id
AND PC.Code ='B'
AND PC.Val > 80
);
这里有几个语义等效的替代方案(因为它们可能返回重复的行):
SELECT P.Name
FROM P, PC
WHERE P.Id = PC.Id
AND PC.Code ='A'
AND PC.Val > 100
AND P.Name IN (
SELECT P1.Name
FROM P AS P1, PC AS PC1
WHERE P1.Id = PC1.Id
AND PC1.Code = 'B'
AND PC1.Val > 80
);
SELECT P.Name
FROM P, PC
WHERE P.Id = PC.Id
AND PC.Code ='A'
AND PC.Val > 100
AND P.Name = ANY (
SELECT P1.Name
FROM P AS P1, PC AS PC1
WHERE P1.Id = PC1.Id
AND PC1.Code = 'B'
AND PC1.Val > 80
);
【讨论】:
【参考方案3】:SELECT P.Name
FROM P
JOIN PC AS P1 ON P.Id = P1.Id AND P1.Cod = 'A' AND P1.Val > 100
JOIN PC AS P2 ON P.Id = P2.Id AND P2.Cod = 'B' AND P2.Val > 80
使用表别名 P1 和 P2 允许您进行 3 路连接。不过,它并不是完全自加入。这次不行。
【讨论】:
【参考方案4】:Select P.Name
from P, PC
where P.Id=PC.Id
and PC.Cod='A' and Val>100
and exists (Select 1 From PC Where Id = P.Id and Cod = 'B' and Val > 80)
【讨论】:
您是否在 Access 中对此进行了测试?我问的原因是Val
和Name
是保留字。【参考方案5】:
Select p.Name
from P p
inner join PC pc1 on p.Id = pc1.Id and pc1.Cod = 'A' and pc1.Val > 100
inner join PC pc2 on p.Id = pc2.Id and pc2.Cod = 'B' and pc2.Val > 80
【讨论】:
不,这行不通,因为 PC.Cod 不能同时是“A”和“B”。您需要第二次加入。 酷,我会投票赞成你取消投票反对的人...... :) 您可以再次点击否决票,它会恢复正常:P 感谢您的警告! 我会这样做,只是我一开始并没有对你投反对票!【参考方案6】:实际上不会使用这个,而是一个替代方案......
SELECT P.Name
FROM P
JOIN PC
ON P.Id = PC.Id
WHERE PC.Cod IN ( 'A', 'B' )
AND Val > 80
GROUP BY P.Id,
P.Name
HAVING MAX(CASE WHEN PC.Cod='A' and Val>100 THEN 1 END) = 1
AND MAX(CASE WHEN PC.Cod='B' and Val>80 THEN 1 END) = 1
或者对于 Microsoft Access,have 子句需要是
HAVING MAX(IIf(PC.Cod='A' and Val>100, 1, 0)) = 1
AND MAX(IIf(PC.Cod='B' and Val>80, 1, 0)) = 1
【讨论】:
不是通过否决票,而是令人难以置信的扭曲。它会起作用,但很难直接翻译需求。 @JonathanLeffler - OP 要求提供替代方案。这确实会通过表格。以上是关于SQL 子查询替代 INTERSECT的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server - Exists 的替代方案(子查询太多)