【问题标题】:How to write query with outer joins and multiple OR criteria?如何编写带有外连接和多个 OR 条件的查询?
【发布时间】:2017-11-17 11:01:14
【问题描述】:

在这个查询中,我们执行从一个大表release 到两个表release_eventrelease_barcode 的外连接。这些必须是外连接,因为在结果中我们希望返回在任一表中都没有条目的版本:

SELECT r.source_uri AS su_on_r, r.title AS t_on_r, release_event.cat, release_barcode.barcode
FROM   release r 
    left join release_event event ON r.source_uri = event.source_uri 
    left join release_barcode barcode ON r.source_uri = barcode.source_uri 
WHERE  ( event.cat IN ( '3145422912' ) AND event.label_name = 'UTV Records' ) 
    OR barcode.barcode IN ( '731454229128' ) 

分析时,这会对release 表执行顺序扫描:

|   ->  Sort  (cost=2169219.61..2169219.65 rows=13 width=66)                                                                                                                  |
|         Sort Key: r.source_uri                                                                                                                                              |
|         ->  Hash Left Join  (cost=721970.94..2169219.37 rows=13 width=66)                                                                                                   |
|               Hash Cond: ((r.source_uri)::text = (barcode.source_uri)::text)                                                                                                |
|               Filter: ((((event.cat)::text = '3145422912'::text) AND ((event.label_name)::text = 'UTV Records'::text)) OR ((barcode.barcode)::text = '731454229128'::text)) |
|               ->  Hash Right Join  (cost=589927.01..1568193.22 rows=11405330 width=88)                                                                                      |
|                     Hash Cond: ((event.source_uri)::text = (r.source_uri)::text)                                                                                            |
|                     ->  Seq Scan on release_event event  (cost=0.00..283078.30 rows=11405330 width=65)                                                                      |
|                     ->  Hash  (cost=324393.56..324393.56 rows=10963956 width=66)                                                                                            |
|                           ->  Seq Scan on release r  (cost=0.00..324393.56 rows=10963956 width=66)                                                                          |
|               ->  Hash  (cost=63125.97..63125.97 rows=2965197 width=60)                                                                                                     |
|                     ->  Seq Scan on release_barcode barcode  (cost=0.00..63125.97 rows=2965197 width=6

如果我只有上述条件之一,查询规划器 (Postgresql) 会理解这一点,使用索引来限制来自 release 的结果:

|   ->  Sort  (cost=42.26..42.27 rows=3 width=66)                                                                               |
|         Sort Key: r.source_uri                                                                                                |
|         ->  Nested Loop  (cost=0.99..42.24 rows=3 width=66)                                                                   |
|               ->  Index Scan using release_barcode_barcode_idx on release_barcode barcode  (cost=0.43..16.47 rows=3 width=47) |
|                     Index Cond: ((barcode)::text = '731454229128'::text)                                                      |
|               ->  Index Scan using release_pkey on release r  (cost=0.56..8.58 rows=1 width=66)                               |
|                     Index Cond: ((source_uri)::text = (barcode.source_uri)::text)         

如何使用外部联接和多个 OR'd 条件重写查询,以首先限制联接的表,并仅在 release 表中查找任何匹配的行?

【问题讨论】:

  • 不相关,但是:外部联接表上的 where 子句将您的外部联接变成内部联接
  • 差不多,有一个 OR。

标签: sql postgresql


【解决方案1】:

加入前过滤:

select 
    source_uri as su_on_r, 
    r.title as t_on_r, 
    event.cat, 
    barcode.barcode
from  
    release r 
    left join (
        select *
        from release_event
        where cat in ('3145422912') and label_name = 'utv records'
    ) event using (source_uri)
    left join (
        select *
        from release_barcode 
        where barcode in ('731454229128') 
    ) barcode using (source_uri)

【讨论】:

  • 执行此操作时,我仍然会在发布表上进行 seq 扫描。估计成本略小。
【解决方案2】:

您可以尝试使用exists 编写此内容:

SELECT r.source_uri as su_on_r, r.title as t_on_r
FROM release r 
WHERE EXISTS (SELECT 1
              FROM release_event re
              WHERE r.source_uri = re.source_uri AND
                    re.cat IN ( '3145422912' ) AND re.label_name = 'UTV Records'
             ) OR
      EXISTS (SELECT 1
              FROM release_barcode rb 
              WHERE r.source_uri = rb.source_uri AND
                    rb.barcode IN ( '731454229128' ) 
             );

对于这些查询,我建议使用release_event(source_uri, cat, label_name)release_barcode(source_uri, barcode) 上的索引。

您还可以使用union 来表达查询(以删除重复项):

SELECT r.source_uri AS su_on_r, r.title AS t_on_r
FROM release r JOIN
     release_event event
     ON r.source_uri = event.source_uri 
WHERE  event.cat IN ( '3145422912' ) AND event.label_name = 'UTV Records' 
UNION
SELECT r.source_uri AS su_on_r, r.title AS t_on_r
FROM release r JOIN
     release_barcode
     ON r.source_uri = barcode.source_uri 
WHERE barcode.barcode IN ( '731454229128' )  ;

【讨论】:

  • 我应该说,我仍然需要来自release_eventrelease_barcode 表的数据,因此需要将它们连接起来......这会影响吗?抱歉,我在示例中使选择语句变得简单,将进行编辑。
  • @DanGravell。 . .只能回答您实际提出的问题。如果您还有其他问题,请作为 another 问题提出。如果您编辑此问题,您将使此答案无效,这可能会导致投票失败。同时,我建议您尝试这些方法,看看性能是否有所提高。
  • 当我运行exists 解决方案时,我仍然会在release 表上进行seq 扫描。
  • @DanGravell。 . .这很有趣——这就是 SQL 优化器的好奇心。
【解决方案3】:

这有点摸不着头脑,但是如果您将 where 条件移动到左连接中,然后测试其中一个/或记录在连接中是否成功:

SELECT
  r.source_uri AS su_on_r, r.title AS t_on_r, release_event.cat, release_barcode.barcode
FROM
  release r 
  left join release_event event ON
    r.source_uri = event.source_uri  and
    event.cat IN ( '3145422912' ) AND 
    event.label_name = 'UTV Records'
  left join release_barcode barcode ON 
     r.source_uri = barcode.source_uri and
     barcode.barcode IN ( '731454229128' ) 
WHERE
  event.source_uri is not null or
  barcode.source_uri is not null

@a_horse_with_no_name 对您的构造实际上如何成为内部连接发表了评论,我认为这将克服这一点。我不确定它是否能得到结果(和你所寻求的性能)。

【讨论】:

  • 这似乎仍在对 release 表进行 seq 扫描,尽管查询的总体估计成本约为原始查询的 1/4。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-15
  • 1970-01-01
  • 2014-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-10
相关资源
最近更新 更多