Эффективная индексация разреженного столбца с нулевым значением в Postgres

Мне нужно проиндексировать столбец Postgres, который состоит в основном из значений NULL. Я не хочу, чтобы значения NULL сохранялись в индексе (чтобы уменьшить индекс и ускорить вставку строк). Однако добавление частичного индекса к IS NOT NULL позволяет мне эффективно искать только все ненулевые значения, а не конкретное ненулевое значение.

Как определить индекс значения в этом столбце, исключающий из индекса только значения NULL?

«Однако добавление частичного индекса IS NOT NULL позволяет мне эффективно искать только все ненулевые значения, а не конкретное ненулевое значение». У меня работает. Пожалуйста, покажите нам, что вы сделали, что привело вас к такому выводу.

jjanes 09.07.2024 16:53
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если большинство значений имеют значение NULL, наиболее эффективным способом поиска NULL является последовательное сканирование таблицы. Таким образом, индекс не поможет вам в таком поиске. Следовательно, вы могли бы также использовать частичный индекс; вы ничего не потеряете.

Начиная с PostgreSQL v13, индексы B-дерева «дедублируют» повторяющиеся значения, и от этого могут выиграть столбцы с большим количеством значений NULL. Таким образом, даже если вы не создадите частичный индекс, индекс будет довольно небольшим (но вы будете платить за изменения индекса всякий раз, когда вставляете NULL).

Ответ принят как подходящий

частичный индекс на IS NOT NULL позволяет мне эффективно искать только все ненулевые значения, а не конкретное ненулевое значение.

Это звучит как одно из этого:

create index on tbl(col is not null);
create index on tbl(col is not null, col);
create index on tbl(col, col is not null);

Но ничто из вышеперечисленного не является частичным индексом . Это было бы

create index on tbl(col)where(col is not null);

И он позволяет вам «эффективно искать определенные ненулевые значения», будучи в точности «индексом значения в этом столбце, который исключает из индекса только значения NULL».


демо на db<>fiddle

select setseed(.42);

create table tbl(col,r)as 
  select case when .5<random() then n::int end as col--50% null `col` values
        ,random() as r
  from generate_series(1,7e5)n 
  where .1<random()
  order by random();

create index idx1_normal_no_deduplication on tbl(col)
  with(deduplicate_items=false);
create index idx2_partial_no_deduplication on tbl(col)
  with(deduplicate_items=false)
  where(col is not null);
create index idx3_normal_deduplicated on tbl(col)
  with(deduplicate_items=true);
create index idx4_partial_deduplicated on tbl(col)
  with(deduplicate_items=true)
  where(col is not null);

Вы можете проверить, насколько меньше частичный индекс по сравнению с обычным:

select indexrelname
     , pg_relation_size(indexrelid)
     , pg_size_pretty(pg_relation_size(indexrelid))
from pg_stat_all_indexes i 
  join pg_class c 
  on i.relid=c.oid 
where i.relname='tbl'
order by 2;
имя_индекса_отношения pg_relation_size pg_size_pretty idx2_partial_no_deduplication 7118848 6952 КБ idx4_partial_dedupliced 7118848 6952 КБ idx3_normal_dedupliced 9289728 9072 КБ idx1_normal_no_deduplication 14180352 14 МБ

Это имеет смысл, учитывая, что 50% col являются null, а все остальные значения уникальны. Это также показывает, что даже при включенной дедупликации частичный индекс все равно может занимать гораздо меньший объем, чем дедуплицированный, нечастичный. Если дедупликация отключена, вы получите индекс на 50 % меньше, и даже при ее включении еще можно сэкономить 25 %. В конце концов, равные значения не пропускаются полностью, они все еще присутствуют, но лучше уплотняются.
Само собой разумеется, что при меньших значениях null разница будет менее выражена.

Оставляем только частичный, дедуплицированный вариант:

explain analyze verboseselect from tbl where col<8e4;
Сканирование только по индексу с использованием idx4_partial_deduplicationd в public.tbl (стоимость = 0,42..9804,20 строк = ширина 209863 = 0) (фактическое время = 0,060..90,552 строк = 36110 циклов = 1)
explain analyze verbose select from tbl where col=8e4;
Собрать (стоимость = 1000,42..7904,08 строк = ширина 3148 = 0) (фактическое время = 81,817..82,158 строк = 0 циклов = 1) Планируется рабочих: 3 Запущено рабочих: 3 -> Сканирование только параллельного индекса с использованием idx4_partial_deduplicationd в public.tbl (стоимость = 0,42..6589,28 строк = ширина 1015 = 0) (фактическое время = 50.420..50.421 строк = 0 циклов = 4)

Учет col is null отключает использование индекса, поскольку именно эти случаи игнорируются индексом (а также потому, что может быть проще выполнить последовательное сканирование, если условия описывают достаточно большую часть набора):

explain analyze verbose select from tbl where col=8e4 or col is null;
Seq Scan в public.tbl (стоимость = 0,00..12539,83 строк = 314781 ширина = 0) (фактическое время = 0,014..102,891 строк = 314082 цикла = 1)

Спасибо, вы исправили многие мои заблуждения!

Luke Hutchison 11.07.2024 04:04

Должен ли мой запрос также иметь явную проверку is not null, чтобы использовать этот индекс, например col is not null and col = 'someValue'?

Luke Hutchison 11.07.2024 06:39

@LukeHutchison Нет, col = 'someValue' уже подразумевает col is not null. Большинство упоминаний col в ваших where условиях заставят Postgres рассмотреть возможность использования этого индекса, за исключением случаев, когда вы добавляете or col is null (не обязательно дословно, это может быть case..else, coalesce() или что-то еще, что учитывает случаи, когда col может быть null), что будет означать вас интересуют значения вне этого индекса.

Zegarek 11.07.2024 14:16

Спасибо, я ценю все время, которое вы потратили на этот ответ!

Luke Hutchison 11.07.2024 19:06

Другие вопросы по теме