Как получить запрос date_part для попадания в индекс? - PullRequest
1 голос
/ 29 марта 2019

Мне еще не удалось получить этот запрос для попадания в индекс вместо полного сканирования - у меня есть другой запрос, который использует date_part ('day', datelocal) для почти идентичной таблицы (в этой таблице только битменьше данных, но той же структуры), и он попадет в индекс, который я создал для столбца datelocal (который является временной меткой без часового пояса).Запрос (этот выполняет параллельное сканирование таблицы и выполняет быструю сортировку памяти):

SELECT
    date_part('hour', datelocal) AS hour,
    SUM(CASE WHEN gender LIKE 'male' THEN views ELSE 0 END) AS male,
    SUM(CASE WHEN gender LIKE 'female' THEN views ELSE 0 END) AS female
FROM reportimpression
WHERE datelocal >= '2-1-2019' AND datelocal < '2-28-2019'
GROUP BY date_part('hour', datelocal)
ORDER BY date_part('hour', datelocal)

Вот еще один, который попал в мой указатель даты:

SELECT
    date_part('day', datelocal) AS day,
    SUM(CASE WHEN gender LIKE 'male' THEN views ELSE 0 END) AS male,
    SUM(CASE WHEN gender LIKE 'female' THEN views ELSE 0 END) AS female
FROM reportimpressionday
WHERE datelocal >= '2-1-2019' AND datelocal < '2-28-2019'
GROUP BY date_trunc('day', datelocal), date_part('day', datelocal)
ORDER BY date_trunc('day', datelocal)

Ударымоя голова об этом!Любые идеи относительно того, как я могу ускорить первый или, по крайней мере, заставить его попасть в индекс?Я пытался создать индекс для поля datelocal, составной индекс для datelocal, пола и представлений, а также индекс выражения для date_part ('hour', datelocal), но ничего из этого не сработало.

Схемы:

-- Table Definition ----------------------------------------------

CREATE TABLE reportimpression (
    datelocal timestamp without time zone,
    devicename text,
    network text,
    sitecode text,
    advertisername text,
    mediafilename text,
    gender text,
    agegroup text,
    views integer,
    impressions integer,
    dwelltime numeric
);

-- Indices -------------------------------------------------------

CREATE INDEX reportimpression_datelocal_index ON reportimpression(datelocal timestamp_ops);
CREATE INDEX reportimpression_viewership_index ON reportimpression(datelocal timestamp_ops,views int4_ops,impressions int4_ops,gender text_ops,agegroup text_ops);
CREATE INDEX reportimpression_test_index ON reportimpression(datelocal timestamp_ops,(date_part('hour'::text, datelocal)) float8_ops);
-- Table Definition ----------------------------------------------

CREATE TABLE reportimpressionday (
    datelocal timestamp without time zone,
    devicename text,
    network text,
    sitecode text,
    advertisername text,
    mediafilename text,
    gender text,
    agegroup text,
    views integer,
    impressions integer,
    dwelltime numeric
);

-- Indices -------------------------------------------------------

CREATE INDEX reportimpressionday_datelocal_index ON reportimpressionday(datelocal timestamp_ops);
CREATE INDEX reportimpressionday_detail_index ON reportimpressionday(datelocal timestamp_ops,views int4_ops,impressions int4_ops,gender text_ops,agegroup text_ops);

Объяснить (проанализировать, буферизировать) вывод:

Finalize GroupAggregate  (cost=999842.42..999859.67 rows=3137 width=24) (actual time=43754.700..43754.714 rows=24 loops=1)
  Group Key: (date_part('hour'::text, datelocal))
  Buffers: shared hit=123912 read=823290
  I/O Timings: read=81228.280
  ->  Sort  (cost=999842.42..999843.99 rows=3137 width=24) (actual time=43754.695..43754.698 rows=48 loops=1)
        Sort Key: (date_part('hour'::text, datelocal))
        Sort Method: quicksort  Memory: 28kB
        Buffers: shared hit=123912 read=823290
        I/O Timings: read=81228.280
        ->  Gather  (cost=999481.30..999805.98 rows=3137 width=24) (actual time=43754.520..43777.558 rows=48 loops=1)
              Workers Planned: 1
              Workers Launched: 1
              Buffers: shared hit=123912 read=823290
              I/O Timings: read=81228.280
              ->  Partial HashAggregate  (cost=998481.30..998492.28 rows=3137 width=24) (actual time=43751.649..43751.672 rows=24 loops=2)
                    Group Key: date_part('hour'::text, datelocal)
                    Buffers: shared hit=123912 read=823290
                    I/O Timings: read=81228.280
                    ->  Parallel Seq Scan on reportimpression  (cost=0.00..991555.98 rows=2770129 width=17) (actual time=13.097..42974.126 rows=2338145 loops=2)
                          Filter: ((datelocal >= '2019-02-01 00:00:00'::timestamp without time zone) AND (datelocal < '2019-02-28 00:00:00'::timestamp without time zone))
                          Rows Removed by Filter: 6792750
                          Buffers: shared hit=123912 read=823290
                          I/O Timings: read=81228.280
Planning time: 0.185 ms
Execution time: 43777.701 ms

1 Ответ

1 голос
/ 29 марта 2019

Итак, оба ваших запроса находятся в разных таблицах (reportimpression против reportimpressionday), поэтому сравнение двух запросов на самом деле не является сравнением.Вы ANALYZE оба?Различные столбцы статистики также могут играть роль.Индекс или раздувание таблицы могут отличаться.Подходит ли большая часть всех строк на февраль 2019 года?И т.д.

Один снимок в темноте, сравните проценты для обеих таблиц:

SELECT tbl, round(share * 100 / total, 2) As percentage
FROM  (
   SELECT text 'reportimpression' AS tbl
        , count(*)::numeric AS total
        , count(*) FILTER (WHERE datelocal >= '2019-02-01' AND datelocal < '2019-03-01')::numeric AS share
   FROM  reportimpression

   UNION ALL
   SELECT 'reportimpressionday'
        , count(*)
        , count(*) FILTER (WHERE datelocal >= '2019-02-01' AND datelocal < '2019-03-01')
   FROM  reportimpressionday
  ) sub;

Является ли значение для reportimpression больше?Тогда он может просто превысить число, для которого индекс должен помочь.

Как правило, ваш индекс reportimpression_datelocal_index on (datelocal) выглядит хорошо, и reportimpression_viewership_index даже разрешает сканирование только по индексу, если автоочисткабьет нагрузку записи на стол.(Хотя impressions & agegroup - просто мертвый груз для этого, и без него будет работать еще лучше.)

Ответ

Вы получили 26.6 percent, and day is 26.4 percent для моегозапрос.Для такого большого процента индексы, как правило, бесполезны вообще .Последовательное сканирование обычно является самым быстрым способом.Только сканирование только по индексу может иметь смысл, если базовая таблица намного больше.(Или у вас есть серьезное раздувание таблиц и менее раздутые индексы, что делает индексы снова более привлекательными.)

Ваш первый запрос может быть только через переломный момент.Попробуйте сузить временные рамки, пока не увидите сканы только по индексу.Вы не увидите (растровое) индексное сканирование с более чем примерно 5% всех подходящих строк (зависит от многих факторов).

Запросы

Как бы то ни было, рассмотрите эти измененные запросы:

SELECT date_part('hour', datelocal)                AS hour
     , SUM(views) FILTER (WHERE gender = 'male')   AS male
     , SUM(views) FILTER (WHERE gender = 'female') AS female
FROM   reportimpression
WHERE  datelocal >= '2019-02-01'
AND    datelocal <  '2019-03-01' -- '2019-02-28'  -- ?
GROUP  BY 1
ORDER  BY 1;

SELECT date_trunc('day', datelocal)                AS day
     , SUM(views) FILTER (WHERE gender = 'male')   AS male
     , SUM(views) FILTER (WHERE gender = 'female') AS female
FROM   reportimpressionday
WHERE  datelocal >= '2019-02-01'
AND    datelocal <  '2019-03-01'
GROUP  BY 1
ORDER  BY 1;

Основные точки

  • При использовании локализованного формата даты как '2-1-2019', пройти через to_timestamp() с явными спецификаторами формата.В противном случае это зависит от настроек локали и может прерываться (тихо) при вызове из сеанса с другими настройками.Вместо этого используйте форматы даты / времени ISO, как показано, которые не зависят от настроек локали.

  • Похоже, вы хотите включить целый месяц февраля.Но ваш запрос не попадает в верхнюю границу.Для одного февраля может быть 29 дней.datelocal < '2-28-2019' исключает также все 28 февраля.Вместо этого используйте datelocal < '2019-03-01'.

  • Группировать и сортировать дешевле по тому же выражению , что и в списке SELECT, если можете.Так что используйте date_trunc() там тоже.Не используйте разные выражения без необходимости.Если вам нужно часть даты в результате, примените ее к сгруппированному выражению, например:

    SELECT date_part('day', date_trunc('day', datelocal)) AS day
    ...
    GROUP  BY date_trunc('day', datelocal)
    ORDER  BY date_trunc('day', datelocal);
    

    Немного больше шумного кода, но быстрее (и, возможно, проще оптимизировать для запросапланировщик тоже).

  • Используйте агрегат FILTER предложение в Postgres 9.4 или новее.Это чище и немного быстрее.См .:

...