Ускорить запрос, в который включены результаты с количеством (*) = 0 - PullRequest
1 голос
/ 28 апреля 2020

У меня есть таблица squitters, среди прочего, столбец parsed_time. Я хочу знать количество записей в час за последние два дня и использовал этот запрос:

SELECT date_trunc('hour', parsed_time) AS hour , count(*) 
FROM squitters 
WHERE parsed_time > date_trunc('hour', now()) - interval '2 day' 
GROUP BY hour 
ORDER BY hour DESC;

Это работает, но часы с нулевыми записями не отображаются в результате. Я хочу иметь часы с нулевыми записями и в результате с счетчиком, равным нулю, поэтому я написал этот запрос, используя функцию generate_series:

SELECT bins.hour, count(squitters.parsed_time)
FROM generate_series(date_trunc('hour', now() - interval '2 day'),  now(), '1 hour') bins(hour)
LEFT OUTER JOIN squitters ON bins.hour = date_trunc('hours', squitters.parsed_time) 
GROUP BY bins.hour
ORDER BY bins.hour DESC;

Это работает, в результате получаются часовые корзины с количеством, равным нулю, но значительно медленнее.

Как можно получить скорость первого запроса при подсчете = ноль результатов второго запроса?

(кстати, есть индекс parsed_time)

Ответы [ 2 ]

1 голос
/ 28 апреля 2020

Вы можете воспользоваться Общими табличными выражениями , чтобы разделить вашу проблему на маленькие куски:

WITH cte AS (
    --First query your table
    SELECT date_trunc('hour', parsed_time) AS sq_hour , count(*) 
    FROM squitters 
    WHERE parsed_time > date_trunc('hour', now()) - interval '2 day' 
    GROUP BY hour 
    ORDER BY hour DESC
), series AS (
    --Create the series without the data returned from 1st query
    SELECT 
        bins.series_hour, 
        0
    FROM 
        generate_series(date_trunc('hour', now() - interval '2 day'),  now(), '1 hour') bins(series_hour) 
    WHERE 
        series_hour not in (SELECT sq_hour FROM cte)
)
--Union the result
SELECT * FROM cte 
UNION 
SELECT * FROM series 
ORDER BY 1
1 голос
/ 28 апреля 2020

Вы можете попытаться изменить условие соединения, чтобы в столбце parsed_time:

SELECT b.hour, COUNT(s.parsed_time) cnt
FROM generate_series(date_trunc('hour', now() - interval '2 day'),  now(), '1 hour') b(hour)
LEFT OUTER JOIN squitters s
    ON  s.parsed_time >= b.hour
    AND s.parsed_time <  b.hours + interval '1 hour'
GROUP BY b.hour
ORDER BY b.hour DESC;

не применялась функция даты. В качестве альтернативы вы можете попробовать использовать коррелированный подзапрос (или боковое соединение) вместо left join - это устраняет необходимость во внешней агрегации:

SELECT 
    b.hour,
    (
        SELECT COUNT(*) 
        FROM squitters s 
        WHERE s.parsed_time >= b.hour AND s.parsed_time <  b.hours + interval '1 hour'
    ) cnt
FROM generate_series(date_trunc('hour', now() - interval '2 day'),  now(), '1 hour') b(hour)
ORDER BY b.hour desc
...