2014-09-06 1 views
0

Query выполняет в микросекундах:Почему добавление функции окна делает этот запрос настолько медленным?

SELECT t1.id 
FROM (SELECT t0.id AS id FROM t0) AS t1 
WHERE NOT (EXISTS (SELECT 1 
     FROM t2 
     WHERE t2.ph_id = t1.id AND t2.me_id = 1 AND t2.rt_id = 4)) 
LIMIT 20 OFFSET 0 

Но запрос B занимает около 25 секунд:

SELECT t1.id, count(*) OVER() AS count 
FROM (SELECT t0.id AS id FROM t0) AS t1 
WHERE NOT (EXISTS (SELECT 1 
     FROM t2 
     WHERE t2.ph_id = t1.id AND t2.me_id = 1 AND t2.rt_id = 4)) 
LIMIT 20 OFFSET 0 

(разница является только один элемент в выбранном пункте - это агрегированный окно)

EXPLAIN является следующим: для A:

Limit (cost=0.00..1.20 rows=20 width=4) 
    -> Nested Loop Anti Join (cost=0.00..3449.22 rows=57287 width=4) 
     Join Filter: (t2.ph_id = t0.id) 
     -> Seq Scan on t0 (cost=0.00..1323.88 rows=57288 width=4) 
     -> Materialize (cost=0.00..1266.02 rows=1 width=4) 
       -> Seq Scan on t2 (cost=0.00..1266.01 rows=1 width=4) 
        Filter: ((me_id = 1) AND (rt_id = 4)) 

И Б:

Limit (cost=0.00..1.45 rows=20 width=4) 
    -> WindowAgg (cost=0.00..4165.31 rows=57287 width=4) 
     -> Nested Loop Anti Join (cost=0.00..3449.22 rows=57287 width=4) 
       Join Filter: (t2.ph_id = t0.id) 
       -> Seq Scan on t0 (cost=0.00..1323.88 rows=57288 width=4) 
       -> Materialize (cost=0.00..1266.02 rows=1 width=4) 
        -> Seq Scan on t2 (cost=0.00..1266.01 rows=1 width=4) 
          Filter: ((me_id = 1) AND (rt_id = 4)) 

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

ответ

3

Ваш исходный запрос может быть записана следующим образом:

SELECT t0.id 
FROM t0 
WHERE NOT EXISTS (SELECT 1 
        FROM t2 
        WHERE t2.ph_id = t1.id AND t2.me_id = 1 AND t2.rt_id = 4 
       ) 
LIMIT 20 OFFSET 0; 

У вас нет order by, поэтому запрос может начать возвращать результаты, поскольку они находятся в наборе результатов. При добавлении функции окна:

SELECT t.0.id, count(*) over() 

Теперь она подсчитывать количество строк в наборе результатов, поэтому он должен генерировать весь набор результатов. Следовательно, вместо того, чтобы просто получать первые двадцать строк, запрос должен генерировать все из них. Это занимает больше времени.

2

Вы можете проверить, как долго COUN (*) принимает и то, что выполнение плана выглядит следующим образом:

SELECT count(*) 
FROM (SELECT t0.id AS id FROM t0) AS t1 
WHERE NOT (EXISTS (SELECT 1 
     FROM t2 
     WHERE t2.ph_id = t1.id AND t2.me_id = 1 AND t2.rt_id = 4)) 

Это может дать вам представление о том, почему это занимает больше времени.

В основном первый запрос содержит только 20 первых записей, которые соответствуют критериям от t0, тогда как второй запрос должен генерировать полный набор записей, соответствующих критериям для их подсчета.

0

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

После запуска команды ...:

ANALYZE; 

... Postgresql смог выбрать более подходящий план запроса, и теперь оба запросы выполняются очень быстро.