【问题标题】:Oracle/SQL - Multijoin optimizationOracle/SQL - 多连接优化
【发布时间】:2012-07-06 20:47:35
【问题描述】:

我正试图解决这个我有两个表的多连接问题

INTERESTED_IN
email           item
-----------------------
bob@email.com   widgetA
karen@email.com widgetA
karen@email.com widgetB
sue@email.com   widgetC
tony@email.com  widgetA
tony@email.com  widgetB
tony@email.com  widgetC


PURCHASED
email           item
-----------------------
bob@email.com   widgetA
karen@email.com widgetA
tony@email.com  widgetB
julie@email.com widgetC

目标:返回interested_in 表和购买表中存在的人的电子邮件和物品,但只返回不在购买表中的物品。所以结果表是这样的

email           item
-----------------------
karen@email.com widgetB
tony@email.com  widgetA
tony@email.com  widgetC

从概念上讲,我知道如何做到这一点,但我很难想出最有效的方法来做到这一点,因为数据集可能非常大。有人能告诉我最好的方法吗?

谢谢!


更新

SELECT email, item
FROM (
    SELECT i.email, i.item
    FROM interested_in i
    INNER JOIN purchased  p ON i.email = p.email
    ) 

MINUS

SELECT email, item
FROM purchased 

【问题讨论】:

  • 为什么sue@email.com widgetC不在结果集中?

标签: sql oracle left-join inner-join self-join


【解决方案1】:

啊,我想念苏。因此,您想要INTERESTED_IN 表中电子邮件存在于PURCHASED 表中的所有行,而不是EMAIL 和ITEM 都存在于PURCHASED 表中的那些行。

最明显的方法是这样的,尽管它需要点击PURCHASED 表两次。

SELECT email, item
  FROM interested_in i
 WHERE EXISTS( SELECT 1
                 FROM purchased p
                WHERE i.email = p.email )
   AND NOT EXISTS( SELECT 1
                     FROM purchased p
                    WHERE i.email = p.email
                      AND i.item  = p.item )

或

SELECT email, item
  FROM interested_in i
 WHERE EXISTS( SELECT 1
                 FROM purchased p
                WHERE i.email = p.email )
MINUS
SELECT email, item
  FROM purchased

我强烈打赌有一个聪明的方法可以做到这一点,同时只打一次PURCHASED 表,尽管这种方法对我来说并不是很明显。

【讨论】:

  • 我喜欢减号的想法,但它会从 Interest_in 表中返回“sue”记录,而且不应该这样。
  • @ypercube - 天啊!是的。错过了AND
  • 我真的很喜欢减号语句的想法。我一直被告知使用存在是不好的,因为内部选择将为每个外部记录执行一次(至少我想我记得被告知)。考虑到这一点,我对解决方案的尝试(更新了我的原始帖子)与您最近的解决方案相比如何?
  • EXISTS 不像您描述的那样工作(至少不总是如此)。不在像 Oracle 这样的严肃 DBMS 中。使用适当的索引,这将很快。
  • @dscl - 您发布的解决方案(实际上应该是您自己问题的答案)将比我的回答中的MINUS 效率低,因为连接将生成多个具有被MINUS 过滤。如果 Tony 在 interested_in 中出现 3 次,在 purchased 中出现 4 次,则解决方案中的内部联接将生成 12 行,如果您使用 EXISTS,则必须在 MINUS 中处理这些行,而不是 4 行。我提供的MINUS 是否会优于我发布的NOT EXISTS 取决于多种因素。
【解决方案2】:

大概不会比上面的好……

select
  a.*
from
  interested_in a
  inner join (
    select distinct
      x.email
    from 
      interested_in x
      inner join purchased y
        on x.email = y.email
    ) valid_emails
    on valid_emails.email = a.email 
  left join purchased b
    on a.email = b.email
    and a.item = b.item
where
  b.email is null
;

结果:

EMAIL           ITEM
tony@email.com  widgetA
tony@email.com  widgetC
karen@email.com widgetB

【讨论】:

    【解决方案3】:

    减法应该更有效。第三种方式完全加入其中购买的物品为空

    【讨论】:

    • 我喜欢减号的想法,但贾斯汀建议它从interested_in 表中返回“sue”记录,它不应该是。
    • 有趣的是,MINUS 的效率似乎低于 EXISTS/NOT EXISTS。无论表是否被索引都是如此。见SQL Fiddle
    【解决方案4】:

    您需要从感兴趣的表格中选择所有内容,并使用左连接过滤掉人们已经购买的商品。

    select * from 
       INTERESTED_IN I left join PURCHASED P 
       on I.EMAIL=P.EMAIL and I.ITEM=P.ITEM
    where P.EMAIL is null
    

    代表上述查询的中间表是:

    I.EMAIL         I.ITEM   P.EMAIL         P.ITEM
    ------------------------------------------------
    bob@email.com   widgetA  bob@email.com   widgetA
    karen@email.com widgetA  karen@email.com widgetA
    karen@email.com widgetB  
    sue@email.com   widgetC
    tony@email.com  widgetA
    tony@email.com  widgetB  tony@email.com  widgetB
    tony@email.com  widgetC
    

    【讨论】:

    • 一开始我也是这么想的,但这是不正确的。打破的要求是“返回感兴趣的表和购买的表中存在的人的电子邮件和项目”。 Sue 没有出现在Purchased 表中。
    猜你喜欢
    • 2018-10-30
    • 2021-12-11
    • 2018-02-26
    • 1970-01-01
    • 2017-04-24
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    • 2012-06-28
    相关资源
    最近更新 更多