Есть ли более эффективный способ написать этот SQL? - PullRequest
1 голос
/ 23 сентября 2019

Есть ли способ написать это более эффективно в Postgres?Я буду повторно использовать это в нескольких других запросах.Таблица A большая, таблица B составляет 1/3 от размера A, C очень мала.

SELECT a.field1, b.field2, c.field3
FROM A a
         LEFT JOIN B b on a.ref_id = b.id
         LEFT JOIN C c on b.other_ref_id = c.id
WHERE a.field1 IN (...)

В плане выполнения показано большое значение для loops для 1-го СЛЕВОГО СОЕДИНЕНИЯ.

Объяснить план:

Gather  (cost=1002.62..1290550.84 rows=856452 width=74) (actual time=0.495..1554.401 rows=850836 loops=1)
  Workers Planned: 2
  Workers Launched: 2
  Buffers: shared hit=4022375 read=234277
  ->  Hash Left Join  (cost=2.62..1203905.64 rows=356855 width=74) (actual time=0.263..1441.760 rows=283612 loops=3)
        Hash Cond: (b.other_ref_id = c.id)
        Buffers: shared hit=4022375 read=234277
        ->  Nested Loop Left Join  (cost=1.13..1202967.39 rows=356855 width=44) (actual time=0.145..1402.434 rows=283612 loops=3)
              Buffers: shared hit=4022316 read=234277
              ->  Parallel Index Scan using some_existing_idx on A a  (cost=0.69..785157.53 rows=356855 width=30) (actual time=0.101..731.991 rows=283612 loops=3)
                    Index Cond: (field1 = ANY ('{1,2,3,4,5,6,7,8}'::bigint[]))
                    Buffers: shared hit=632106 read=225426
              ->  Index Scan using b_pkey on B b  (cost=0.44..1.17 rows=1 width=22) (actual time=0.002..0.002 rows=1 loops=850836)
                    Index Cond: (id = a.ref_id)
                    Buffers: shared hit=3390210 read=8851
        ->  Hash  (cost=1.22..1.22 rows=22 width=34) (actual time=0.024..0.024 rows=22 loops=3)
              Buckets: 1024  Batches: 1  Memory Usage: 10kB
              Buffers: shared hit=3
              ->  Seq Scan on C c  (cost=0.00..1.22 rows=22 width=34) (actual time=0.012..0.014 rows=22 loops=3)
                    Buffers: shared hit=3
Planning Time: 5.382 ms
Execution Time: 1581.816 ms

1 Ответ

0 голосов
/ 24 сентября 2019

Соединение левого вложенного цикла между a и b, вероятно, является наиболее эффективным методом здесь.Существует 850000 циклов, но каждое из этих исполнений занимает всего 0,002 миллисекунды, что составляет в общей сложности около 1,9 секунды.Это выполняется тремя рабочими параллельно, поэтому фактическое время составляет около 0,6 секунды.

Это вместе с 0,7 секундами из параллельного сканирования индекса на a составляет время выполнения.

Альтернативой было бы выполнить хеш-соединение между a и b, что потребовало бы последовательного сканирования на b и большого хеш-кода.Либо последовательное сканирование было бы более дорогим, либо work_mem был настроен слишком маленьким, чтобы содержать результирующий хэш.

Единственный шанс для улучшения - повысить work_mem и посмотреть, не станет ли выполнение немного быстрее таким образом..

Чтобы проверить правильность моего анализа, попробуйте

SET enable_nestloop = off;

, а затем снова выполните запрос.Если это замедляет выполнение, PostgreSQL поступил правильно.

...