Почему следующее соединение значительно увеличивает время запроса?

У меня есть звездная схема здесь, и я запрашиваю таблицу фактов и хотел бы присоединиться к одной очень маленькой таблице измерений. Я не могу объяснить следующее:

EXPLAIN ANALYZE SELECT 
 COUNT(impression_id), imp.os_id 
 FROM bi.impressions imp 
 GROUP BY imp.os_id;
 QUERY PLAN
 --------------------------------------------------------------------------------------------------------------------------------------
 HashAggregate (cost=868719.08..868719.24 rows=16 width=10) (actual time=12559.462..12559.466 rows=26 loops=1)
 -> Seq Scan on impressions imp (cost=0.00..690306.72 rows=35682472 width=10) (actual time=0.009..3030.093 rows=35682474 loops=1)
 Total runtime: 12559.523 ms
 (3 rows)

Это занимает ~ 12600 мс, но, конечно, нет связанных данных, поэтому я не могу "разрешить" imp.os_id что-то значимое, поэтому добавляю соединение:

EXPLAIN ANALYZE SELECT 
 COUNT(impression_id), imp.os_id, os.os_desc 
 FROM bi.impressions imp, bi.os_desc os 
 WHERE imp.os_id=os.os_id 
 GROUP BY imp.os_id, os.os_desc;
 QUERY PLAN
 --------------------------------------------------------------------------------------------------------------------------------------------
 HashAggregate (cost=1448560.83..1448564.99 rows=416 width=22) (actual time=25565.124..25565.127 rows=26 loops=1)
 -> Hash Join (cost=1.58..1180942.29 rows=35682472 width=22) (actual time=0.046..15157.684 rows=35682474 loops=1)
 Hash Cond: (imp.os_id = os.os_id)
 -> Seq Scan on impressions imp (cost=0.00..690306.72 rows=35682472 width=10) (actual time=0.007..3705.647 rows=35682474 loops=1)
 -> Hash (cost=1.26..1.26 rows=26 width=14) (actual time=0.028..0.028 rows=26 loops=1)
 Buckets: 1024 Batches: 1 Memory Usage: 2kB
 -> Seq Scan on os_desc os (cost=0.00..1.26 rows=26 width=14) (actual time=0.003..0.010 rows=26 loops=1)
 Total runtime: 25565.199 ms
 (8 rows)

Это эффективно удваивает время выполнения моего запроса. Мой вопрос в том, что я оставил на картинке? Я бы подумал, что такой небольшой поиск не вызвал огромных различий во времени выполнения запроса.

3 ответа

Переписанный (рекомендуется) явный синтаксис ANSI JOIN:

SELECT COUNT(impression_id), imp.os_id, os.os_desc 
FROM bi.impressions imp
JOIN bi.os_desc os ON os.os_id = imp.os_id
GROUP BY imp.os_id, os.os_desc;

Прежде всего, ваш второй запрос может быть неправильным, если в os_desc для каждой строки в показах найдено более одного или меньше одного совпадения. Это можно исключить, если у вас есть ограничение внешнего ключа на os_id на месте, что гарантирует ссылочную целостность, плюс ограничение NOT NULL на bi.impressions.os_id. Если это так, на первом этапе упростите:

SELECT COUNT(*) AS ct, imp.os_id, os.os_desc 
FROM bi.impressions imp
JOIN bi.os_desc os USING (os_id)
GROUP BY imp.os_id, os.os_desc;

count(*) немного быстрее, чем count(column). И добавьте псевдоним столбца для счета. Быстрее, тем не менее:

SELECT os_id, os.os_desc, sub.ct
FROM (
 SELECT os_id, COUNT(*) AS ct
 FROM bi.impressions
 GROUP BY 1
 ) sub
JOIN bi.os_desc os USING (os_id)

Группируйте сначала, присоединитесь позже. Подробнее здесь: Совокупность одного столбца в запросе со многими столбцами PostgreSQL - порядок по массиву


Следующий запрос решает проблему без увеличения времени выполнения запроса. Вопрос по-прежнему заключается в том, почему время выполнения значительно увеличивается с добавлением очень простого соединения, но это может быть специфический вопрос Postgres, и кто-то с большим опытом работы в этой области может в конце концов ответить на него.

WITH 
 OSES AS (SELECT os_id,os_desc from bi.os_desc) 
SELECT 
 COUNT(impression_id) as imp_count, 
 os_desc FROM bi.impressions imp, 
 OSES os 
WHERE 
 os.os_id=imp.os_id 
GROUP BY os_desc 
ORDER BY imp_count;


HashAggregate (cost=868719.08..868719.24 rows=16 width=10)
HashAggregate (cost=1448560.83..1448564.99 rows=416 width=22)

Хмм, ширина от 10 до 22 удваивается. Возможно, вам следует присоединиться после группировки, а не раньше?

licensed under cc by-sa 3.0 with attribution.