如何使用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 existswhere not exists 来实现这一点,因为我需要编写关系演算,其中where not inwhere 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')

如果有人可以在没有countgroup by 的情况下将上述查询转换为where existswhere 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 子句,因为我想将其转换为关系演算,其中这些仅在存在和不存在的情况下可用。 嗯,existsin 在实现方面非常相似,所以我会看看我是否可以使用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?

MySQL 中可能存在或不存在的引用数据

在 dplyr 中,如何通过可能存在或不存在的列连接数据框?

在 c# 中,如何测试/获取/设置可能存在或不存在的注册表项?

如何使用一条mysql语句实现如果查询到的数据不存在就创建

JQ:如果字段为空或不存在,如何删除?