Сделать порядок обложек Postgresql ИНДЕКС по выражению функции - PullRequest
0 голосов
/ 18 июня 2020

У меня есть несколько подобных запросов, в которых некоторые предложения WHERE всегда одинаковы (поэтому я могу создавать частичные индексы на их основе), а некоторые из них являются переменными, поэтому я могу добавлять их в индексы. Запросы выглядят примерно так:

SELECT "cars"."id"
FROM "cars"
WHERE "cars"."sales_state" = 'onsale' -- fixed
  AND (cars.is_disabled IS NOT TRUE) -- fixed
  AND "cars"."id" != 1243221 -- always present but different values
  AND (cars.featuring_score IS NOT NULL) -- fixed
  AND (cars.price >= 12372) -- always present but different values
  AND (cars.price <= 18293) -- always present but different values
  AND ("cars"."body" IN ('Hatchback', -- some times present with different values
                         'Sedan')
       OR "cars"."body" IS NULL)
  AND "cars"."fuel" IN ('Benzin', -- some times present with different values
                        'Diesel')
  AND (cars.year >= 2005) -- some times present with different values
  AND (cars.year <= 2010) -- some times present with different values
  AND (cars.km >= 0) -- some times present with different values
  AND (cars.km <= 100000) -- some times present with different values
  AND (cars.images_count > 0) -- fixed
  AND "cars"."featuring_score" IS NOT NULL -- fixed
ORDER BY 
  CASE -- fixed
    WHEN cars.featuring_score < 'C' THEN 1
    WHEN cars.featuring_score = 'C' THEN 2
    WHEN cars.featuring_score > 'C' THEN 3
    ELSE 4
  END,
  CASE -- fixed
    WHEN cars.au_rating >= 3 THEN 1
    WHEN cars.au_rating = 0 THEN 2
    WHEN cars.au_rating = 2 THEN 3
    WHEN cars.au_rating = 1 THEN 4
    ELSE 6
  END
  -- Then comes a whole bunch of other ORDERING criteria which all vary
LIMIT 100;

Я пробовал добавить два индекса:

CREATE INDEX index_cars_test_1
    ON cars USING btree
    (year ASC NULLS LAST, fuel COLLATE pg_catalog."default" ASC NULLS LAST, price ASC NULLS LAST, km ASC NULLS LAST)
    TABLESPACE pg_default
    WHERE images_count > 0 AND sales_state::text = 'onsale'::text AND is_disabled IS NOT TRUE AND featuring_score IS NOT NULL;

Этот работает несколько быстро, но не так быстро, как я бы sh. Я думал о том, могу ли я также включить предложение order by в индекс, чтобы сделать его быстрее. Я пробовал следующее:

CREATE INDEX index_cars_for_featured_carousels_v6
    ON cars USING btree
    ((
CASE
    WHEN featuring_score::text < 'C'::text THEN 1
    WHEN featuring_score::text = 'C'::text THEN 2
    WHEN featuring_score::text > 'C'::text THEN 3
    ELSE 4
END) ASC NULLS LAST, (
CASE
    WHEN au_rating >= 3 THEN 1
    WHEN au_rating = 0 THEN 2
    WHEN au_rating = 2 THEN 3
    WHEN au_rating = 1 THEN 4
    ELSE 6
END) ASC NULLS LAST, price ASC NULLS LAST, year ASC NULLS LAST, fuel COLLATE pg_catalog."default" ASC NULLS LAST, km ASC NULLS LAST)
    TABLESPACE pg_default
    WHERE sales_state::text = 'onsale'::text AND is_disabled IS NOT TRUE AND featuring_score IS NOT NULL AND images_count > 0;

Однако этот индекс не используется, даже если я сначала проанализировал таблицу. Можно ли вообще эффективно включить порядок в индексе или мне стоит попробовать что-нибудь еще?

Кстати, я на PG 11.

Ответы [ 2 ]

1 голос
/ 18 июня 2020

Вы говорите, что критерии «года» присутствуют лишь иногда, поэтому я, вероятно, не стал бы вести индекс по этому столбцу.

Но поскольку некоторые из ваших столбцов являются диапазонами или IN-списками, вы можете быть лучше использовать несколько индексов с одним столбцом (но все с одним и тем же условием WHERE), и пусть PostgreSQL объединит их с BitmapAnd.

Многоколоночный индекс GiST (с использованием расширения btree_gist) по столбцам с критериями диапазона может быть полезными, но обычно они меня разочаровывают. Особенно, сколько времени уходит на их сборку.

Что делать со строками, которые не соответствуют images_count > 0 AND sales_state::text = 'onsale'::text AND is_disabled IS NOT TRUE AND featuring_score IS NOT NULL? Может быть, вы могли бы просто удалить их, или заархивировать, или отделить от остальных строк.

Одна эвристика c, которую вы могли бы использовать, - это запуск запроса с предложением WHERE, содержащим featuring_score < 'C' and au_rating >= 3, с соответствующим индексом WHERE. Затем, если у вас меньше 10 строк, выбросьте результат и вместо этого запустите исходный запрос.

Если вы можете клонировать свою базу данных и обновить клон до 13BETA1, было бы интересно посмотреть, будет ли новая добавочная сортировка функция будет использоваться с вашим 2-м индексом, и если да, то насколько хорошо она работает.

При отсутствии LIMIT, сколько строк вы ожидаете, что это семейство запросов вернет при «типичном» использовании?

Просмотр вывода EXPLAIN (ANALYZE, BUFFERS) для некоторых типичных запросов (как с LIMIT, так и без него) не повредит.

1 голос
/ 18 июня 2020

Вы должны сделать выбор: либо создать индекс, который ускоряет выполнение условий WHERE, а затем выполнить явную сортировку, или создать индекс, поддерживающий предложение ORDER BY . У вас не может быть обоих (в этом случае).

  • Индекс для предложения WHERE хорош, если фильтры являются выборочными, то есть исключают много строк.

    Поскольку вы говорите, что большинство условий являются необязательными, лучшее, что вы могли бы придумать, это

    CREATE INDEX ON cars (price)
    WHERE sales_state = 'onsale'
      AND is_disabled IS NOT TRUE
      AND featuring_score IS NOT NULL
      AND images_count > 0
      AND featuring_score IS NOT NULL;
    
  • Индекс для предложения ORDER BY является вариантом, если указанный выше индекс не удаляет достаточно строк, чтобы быть полезными. Для этого вам нужно полностью проиндексировать предложение ORDER BY. Но поскольку вы говорите, что предложение ORDER BY может меняться, это не вариант.

    С PostgreSQL v13 или лучше вы можете попробовать индексировать фиксированные части предложения ORDER BY и надеяться для инкрементной сортировки, но я сомневаюсь, что это победит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...