如何使用mysql中存在或不存在的地方实现相交
Posted
技术标签:
【中文标题】如何使用mysql中存在或不存在的地方实现相交【英文标题】:How to implement intersect using where exists or not exists in mysql 【发布时间】:2014-04-14 11:24:57 【问题描述】:我的桌子是:
customer(cid,name,city,state)
orders(oid,cid,date)
product(pid,productname,price)
lineitem(lid,pid,oid,number,totalprice)
我要选择城市'X'的所有客户购买的产品。这意味着我需要与居住在'X'城市的所有客户购买的产品相交
示例:如果有 3 个客户 c1、c2 和 c3,我的答案是 c1.product(intersect)c2.product(intersect)c3.product
我只想使用where exists
或where not exists
来实现这一点,因为我需要编写关系演算,其中where not in
或where in
不可用。我的部分查询是这样的:
select
*
from
product p,
lineitem l,
customer c1
where
exists(
select
*
from
customer c,
orders o
where
o.cid=c.cid and
c.city='X' and
l.oid=o.oid and
l.pid=p.pid and
c1.cid=c.cid)
上面的查询给了我住在 X 市的所有客户的 pid,cid,oid,lid,totalprice,city,productname。现在我需要弄清楚如何选择所有客户共有的产品。
注意:
我不能使用任何聚合函数,因为它在关系微积分中不可用。我有一个使用聚合函数的有效查询,那就是
select
p.productname
from
product p,
orders s,
lineitem l,
customer c
where
l.pid=p.pid and
l.oid=s.oid and
c.cid=s.cid and
c.city='X'
group by
p.productname
having
count(distinct c.cid)=(select count(*) from customer c1 where c1.city='X')
如果有人可以在没有count
和group by
的情况下将上述查询转换为where exists
或where not exists
形式,那就没问题了。
我确信可以做到这一点,因为我可以在关系代数中做到这一点,并且根据Codd's theorom tuple 关系演算和关系代数在逻辑上是等价的,任何用一个表示的查询都可以用另一个表示。因为关系代数和关系演算不支持聚合函数,查询可以在没有聚合函数的sql中表达。
【问题讨论】:
你要的叫Relational Division,NOT EXISTS基本就是Codd的查询,见simple-talk.com/sql/t-sql-programming/… 【参考方案1】:这是我的答案。
我在http://www.sqlfiddle.com/#!2/f2fb85/1创建了一个sqlfiddle,你可以试试。
查询是:
SELECT p.*
FROM product p
WHERE NOT EXISTS (
SELECT c.cid
FROM customer c
WHERE NOT EXISTS (
SELECT l.lid
FROM lineitem l
JOIN orders o ON o.oid = l.oid
WHERE l.pid = p.pid
AND o.cid = c.cid
)
AND c.city = 'X'
) AND EXISTS (
SELECT c.cid
FROM customer c
WHERE c.city = 'X'
)
【讨论】:
我认为AND EXISTS
的第二部分是多余的。干得好。
@Mehran 我不认为 AND EXISTS 是多余的,否则,例如,每个从未购买过的产品都会被选中
您可以在您提供的小提琴中轻松测试它。我也怀疑过,但相信我,不需要。
您查询的第一部分是说:选择所有您找不到没有购买该产品的客户的产品。我相信这就是我们所需要的。
@marcosh 再想一想,如果城市中没有客户,这个新查询将返回所有产品!因为我们说的是if there's no customer who didn't buy it
,如果根本没有客户,那肯定没有人不买!很抱歉,需要 AND
条件来确保该城市存在一些客户。【参考方案2】:
根据您的 cmets,我已将之前的答案替换为不使用 IN
的答案。这个利用了多级相关子查询:
select p.*
from product p
where exists (
select *
from customer c
where c.city = 'x'
and exists (
select *
from lineitem l
where l.pid = p.pid
and exists (
select *
from orders o
where o.oid = l.oid
and o.cid = c.cid
)
)
)
and not exists(
select *
from customer c
where c.city = 'x'
and not exists (
select *
from lineitem l
where l.pid = p.pid
and exists (
select *
from orders o
where o.oid = l.oid
and o.cid = c.cid
)
)
)
SQL Fiddle here.
【讨论】:
谢谢,但我不想使用 where in 和 not in 子句,因为我想将其转换为关系演算,其中这些仅在存在和不存在的情况下可用。 嗯,exists
和 in
在实现方面非常相似,所以我会看看我是否可以使用exists
来重写它。【参考方案3】:
select * from product p where not exists(
select *
from customer c
where c.city = 'x'
and not exists (
select *
from lineitem l
where l.pid = p.pid
and exists (
select *
from orders o
where o.oid = l.oid
and o.cid = c.cid
)
)
)
关系演算:
T| ∃pЄproduct (¬(∃cЄcustomer(c.city="Newark")^¬(∃lЄlineitem(l.pid=p.pid)^∃oЄorders(o.oid=l.oid^o.cid=c.cid))))
关系代数:
【讨论】:
【参考方案4】:ANSI-Sql 缺少集合论universal quantifier,需要use rewrite roles to get existential quantifier:
∀ Universal quantifier (For all . . .)
∃ Existential quantifier (There exists . . .)
示例:
(∀c ∈ CUSTOMER)
⇔ /*Add double negation */
¬¬(∀c ∈ CUSTOMER)
⇔ /*Bring one negation into the quantification, quantifier changes */
¬(∃c ∈ CUSTOMER)
sql翻译:
SELECT p.*
FROM product p
WHERE NOT EXISTS (
SELECT c.cid
FROM customer c
WHERE c.city = 'X'
AND NOT EXISTS
(
SELECT o.oid
FROM orders o
JOIN lineitem l ON l.oid = o.oid
WHERE l.pid = p.pid
AND o.cid = c.cid
)
)
Fiddle-Demo
【讨论】:
以上是关于如何使用mysql中存在或不存在的地方实现相交的主要内容,如果未能解决你的问题,请参考以下文章
如何检查 ReportControl 对象上是不是存在 Reportid 或不使用 *ngif?
在 dplyr 中,如何通过可能存在或不存在的列连接数据框?