Может ли PostgreSQL использовать нулевые значения в своих индексах?

10

Я читал эту книгу, в которой говорится, что

База данных предполагает, что Indexed_Col IS NOT NULL охватывает слишком большой диапазон, чтобы быть полезным, поэтому база данных не будет приводить к индексу из этого условия.

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

Далее в беге EXPLAIN ANALYZEна SELECTзапрос, я обнаружил , что ни один из моих индексов не используют, даже тогда , когда все права, они должны быть.

Таким образом, мой вопрос:

Предположим, что есть таблица, в которой есть столбец, определение столбца которого содержит «NOT NULL», и существует индекс, который охватывает этот столбец. Будет ли этот индекс использоваться в запросе к этой таблице, где столбцы являются частью запроса?

Подобно:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;
FuriousFolder
источник

Ответы:

9

PostgreSQL, безусловно, может использовать индекс для IS NOT NULL. Я не вижу никаких предположений планировщика запросов относительно этого условия.

Если нулевая дробь для column ( pg_statistic.stanullfrac) достаточно мала, чтобы предположить, что индекс является избирательно полезным для запроса, PostgreSQL будет использовать индекс.

Я не могу понять, что вы пытаетесь сказать:

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

Конечно, индекс не будет использоваться для IS NOT NULLусловия в NOT NULLстолбце. Это всегда соответствует 100% строк, поэтому seqscan почти всегда будет намного быстрее.

PostgreSQL не будет использовать индекс, если индекс не отфильтровывает большую часть строк для запроса. Единственное вероятное исключение - когда вы запрашиваете набор столбцов, охватываемых одним индексом, в порядке, соответствующем порядку индекса. Тогда PostgreSQL может выполнять сканирование только по индексу. Например, если есть индекс, t(a, b, c)и вы:

select a, b FROM t ORDER BY a, b, c;

PostgreSQL может использовать ваш индекс, даже если строки не отфильтрованы, потому что он только должен прочитать индекс и может пропустить чтение кучи, избежать сортировки и т. Д.

Крейг Рингер
источник
Это все верно с PG 9.0
eradman
1
И даже в пустом столбце запрос с условием WHERE column IS NOT NULLможет не использовать индекс, потому что, как говорится в книге: «охватывает слишком большой диапазон, чтобы быть полезным». Если 90% значений не являются нулевыми, seqscan, вероятно, тоже будет быстрее.
ypercubeᵀᴹ
В точку. Это возможно, но только если большая часть таблицы равна нулю. Часто в этом случае частичный индекс в любом случае является лучшим выбором.
Крейг Рингер
Да. Я пытался сказать, что (насколько я понимаю) часть «охватывает слишком большой диапазон» относится к индексу, но касается конкретного условия, а не индекса в целом.
ypercubeᵀᴹ
2
@FuriousFolder Хех, здесь слишком много отрицаний. PostgreSQL не будет использовать индекс для NOT NULLстолбца для IS NOT NULLзапроса, если этот индекс также не полезен для других частей WHEREпредложения, фильтров объединения и т. Д. Или не может использоваться для упорядоченного сканирования только по индексу. Другими словами, он будет полностью игнорировать избыточность IS NOT NULLв NOT NULLстолбце и будет делать выбор индекса, основываясь на других деталях. (См. Редактирование, повторное сканирование только по индексу).
Крейг Рингер,
2

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

Охватывает Oracle, DB2 и SQL Server

Так что я бы не поверил, что он станет хорошим источником советов по PostgreSQL в частности. Каждая СУБД может быть на удивление разной!

Я немного озадачен вашим первоначальным вопросом, но вот пример, показывающий, что раздел книги не на 100% правильный. Чтобы избежать путаницы, вот весь соответствующий параграф, вы можете увидеть его в Поиске книг Google .

База данных предполагает, что Indexed_Col IS NOT NULL охватывает слишком большой диапазон, чтобы быть полезным, поэтому база данных не будет приводить к индексу из этого условия. В редких случаях наличие любого ненулевого значения настолько редко, что сканирование диапазона индекса по всем возможным ненулевым значениям выгодно. В таких случаях, если вы можете определить безопасный нижний или верхний предел диапазона всех возможных значений, вы можете включить сканирование диапазона с условием, например Positive_ID_Column> -1 или Date_Column> TO_DATE ('0001/01/01' , «ГГГГ / ММ / ДД»).

Postgres может на самом деле (в следующем надуманном случае) использовать индекс для удовлетворения IS NOT NULLзапросов без добавления полей сканирования диапазона, как предложено Positive_ID_Column > -1. См. Комментарии на вопросы Крейга о том, почему Postgres выбирает этот индекс в данном конкретном случае, и примечание об использовании частичных индексов.

CREATE TABLE bar (a int);
INSERT INTO bar (a) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO bar (a) VALUES (1);
CREATE INDEX bar_idx ON bar (a);

EXPLAIN ANALYZE SELECT * FROM bar WHERE a IS NOT NULL;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using bar_idx on bar  (cost=0.42..8.44 rows=1 width=4) (actual time=0.094..0.095 rows=1 loops=1)
   Index Cond: (a IS NOT NULL)
   Heap Fetches: 1
 Total runtime: 0.126 ms
(4 rows)

Кстати, это Postgres 9.3, но я считаю, что результаты будут примерно одинаковыми на 9.1, хотя он не будет использовать «сканирование только по индексу».

Редактировать: я вижу, что вы прояснили свой первоначальный вопрос, и вы, вероятно, задаетесь вопросом, почему Postgres не использует индекс в простом примере, таком как:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

Возможно, потому что у вас нет строк в таблице. Так что добавьте некоторые тестовые данные и ANALYZE my_table;.

Джош Купершмидт
источник
В описании этой книги (выделено мое): «Автор Дэн Тау описывает метод экономии времени, который он разработал для нахождения оптимального плана выполнения - быстро и систематически - независимо от сложности SQL или используемой платформы базы данных ». возможно, вы упустили # 1 вопроса, а именно, что столбец определен как NOT NULL, а не то, что запрос использует в IS NOT NULLкачестве условия индекса. Это в комментариях, на которые вы ссылались, но я обновлю вопрос, чтобы включить его.
FuriousFolder
Кроме того, сама книга не зависит от языка: единственные части, относящиеся к DMBS, посвящены отображению планов запросов, которые Postgres делает довольно просто :)
FuriousFolder
1
@FuriousFolder столбец определен как NOT NULL, но эта часть (в вашем вопросе из книги): «что Indexed_Col IS NOT NULL охватывает ...» относится к условию where, а не к определению столбца. Хотя трудно быть уверенным, потому что это вне контекста. Возможно, вам следует включить весь (предыдущий) абзац из книги.
ypercubeᵀᴹ
-1

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

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

Но для нескольких строк дешевле пропустить телефонную книгу и выполнить итерации по всем строкам в основной таблице. По моему опыту переломный момент составляет около 100 строк.

Andomar
источник
«Индексы похожи на телефонную книгу, которая переводит столбец в местоположение строки. Если вы ищете только несколько строк, имеет смысл поискать каждую строку в телефонной книге, а затем искать строку в основной таблице». Фактически, индексы похожи на меньшие телефонные книги, которые обновляются каждый раз, когда обновляется телефонная книга, которую они индексируют. Вы знаете, что каждый раз, когда вы открываете телефонную книгу меньшего размера, вы найдете всю информацию, описанную в ее условии индексации. Например , все люди назвали «откровенным» на индексной таблицы: CREATE INDEX ix_frank ON people(name) WHERE name ='frank'.
FuriousFolder
Это позволяет индексировать только сканирование будет гораздо гораздо быстрее, так как вы можете прочитать всю «меньшую телефонную книгу» в память, которая не осуществим с выложены таблицами многомиллионных.
FuriousFolder
@FuriousFolder: вы описываете сканирование только по индексу. Но ОП говорит, что его индексы не используются, что не произойдет, если сканирование только по индексу удовлетворит запрос.
Andomar
Andomar ... Я есмь ОП, ха - ха. Моя цель именно это; чтобы этот запрос использовал сканирование только по индексу. С тех пор я добился этого, так как Крейг пояснил , что Postgres является возможность использовать индекс на колонке , где определение столбца включает в себя NOT NULL
FuriousFolder