Postgres - это правильный способ создания частичного индекса для логического столбца? - PullRequest
6 голосов
/ 15 декабря 2011

У меня есть следующая таблица:

CREATE TABLE recipemetadata
(
  --Lots of columns
  diet_glutenfree boolean NOT NULL,
);

Большинство каждой строки будет установлено на FALSE, если кто-то не придумает какую-нибудь безумную новую безглютеновую диету, которая охватит всю страну.

Мне нужно иметь возможность очень быстро запрашивать строки, в которых это значение истинно. Я создал индекс:

CREATE INDEX IDX_RecipeMetadata_GlutenFree ON RecipeMetadata(diet_glutenfree) WHERE diet_glutenfree;

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

Должен ли я добавить оператор к предложению WHERE, или этот синтаксис совершенно допустим? Надеемся, что это не один из тех очень простых вопросов RTFM, на которые проголосуют 30 раз.

UPDATE:

Я пошел дальше и добавил 10000 строк в RecipeMetadata со случайными значениями. Затем я сделал АНАЛИЗ на столе и РЕИНДЕКС, чтобы быть уверенным. Когда я запускаю запрос:

select recipeid from RecipeMetadata where diet_glutenfree;

Я получаю:

'Seq Scan on recipemetadata  (cost=0.00..214.26 rows=5010 width=16)'
'  Filter: diet_glutenfree'

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

Если я это сделаю:

select recipeid from RecipeMetadata where not diet_glutenfree;

Я получаю:

'Seq Scan on recipemetadata  (cost=0.00..214.26 rows=5016 width=16)'
'  Filter: (NOT diet_glutenfree)'

Так что, несмотря ни на что, этот индекс не используется.

Ответы [ 2 ]

4 голосов
/ 15 декабря 2011

Я подтвердил, что индекс работает как ожидалось.

Я заново создал случайные данные, только на этот раз установил diet_glutenfree в random() > 0.9, так что вероятность on бита составляет всего 10%.

Затем я заново создал индексы и повторил запрос.

SELECT RecipeId from RecipeMetadata where diet_glutenfree;

Возвращает:

'Index Scan using idx_recipemetadata_glutenfree on recipemetadata  (cost=0.00..135.15 rows=1030 width=16)'
'  Index Cond: (diet_glutenfree = true)'

И

SELECT RecipeId from RecipeMetadata where NOT diet_glutenfree;

Возвращает:

'Seq Scan on recipemetadata  (cost=0.00..214.26 rows=8996 width=16)'
'  Filter: (NOT diet_glutenfree)'

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

Однако, я думаю, я бы получил эти точные результаты по полному индексу столбца. Есть ли способ проверить количество строк, проиндексированных в частичном индексе?

UPDATE

Индекс составляет около 40 КБ. Я создал полный индекс для того же столбца, и его размер превышает 200 КБ, поэтому, похоже, он определенно является частичным.

1 голос
/ 17 декабря 2011

Индекс для однобитного поля не имеет смысла. Чтобы понять решения, принятые планировщиком, вы должны думать с точки зрения страниц, а не с точки зрения строк.

Для страниц 8K и (оцененного) размера строки 80, на каждой странице есть 100 строк. Предполагая случайное распределение, вероятность того, что страница состоит только из строк со значением true, пренебрежимо мала, pow (0.5, 100), около 1e-33, IICC. (и то же самое для 'false', конечно). Таким образом, для запроса на gluten_free == true, каждая страница должна быть выбрана в любом случае, а затем отфильтрована. Использование индекса приведет только к извлечению больше страниц (: индекс).

...