Сканирование индекса для сравнения нескольких столбцов - неравномерный порядок столбцов индекса - PullRequest
2 голосов
/ 10 апреля 2019

Этот вопрос тесно связан с Принудительное сканирование индекса для сравнения нескольких столбцов

Решение там идеально, но, кажется, работает, только если все столбцы индекса имеют одинаковый порядок. Этот вопрос отличается тем, что столбец b здесь desc , и этот факт не позволяет использовать синтаксис строки для решения той же проблемы. Вот почему я ищу другое решение.

Предположим, что индекс построен для 3 столбцов (a asc, b DESC, c asc), я хочу, чтобы Postgres:

  1. найти ключ [a = 10, b = 20, c = 30] в этом B-дереве,
  2. сканировать следующие 10 записей и возвращать их.

Если индекс имеет только один столбец, решение очевидно:

select * from table1 where a >= 10 order by a limit 10

Но если столбцов больше, решение становится намного более сложным. Для 3 столбцов:

select * from table1
where a > 10 or (a = 10 and (b < 20 or b = 20 and c <= 30))
order by a, b DESC, c
limit 10;

Как мне сказать Postgres, что я хочу эту операцию?

И могу ли я быть уверен, что даже для этих сложных запросов для 2+ столбцов оптимизатор всегда поймет, что он должен выполнить сканирование диапазона? Почему?

Ответы [ 2 ]

2 голосов
/ 10 апреля 2019

PostgreSQL очень тщательно реализует кортежи (в отличие от половины реализаций, найденных в Oracle, DB2, SQL Server и т. Д.).Вы можете написать свое условие, используя «неравенство кортежей», например:

select * 
from table1
where (a, -b, c) >= (10, -20, 30)
order by a, -b, c
limit 10

Обратите внимание, что, поскольку второй столбец находится в порядке убывания, вы должны «инвертировать» его значение во время сравнения.Вот почему оно выражается как -b, а также -20.Это может быть непросто для нечисловых столбцов, таких как даты, столбцы, большие объекты и т. Д.

Наконец, использование индекса со значением столбца -b все еще возможно, если вы создаете специальный индекс, например:

create index ix1 on table1 (a, (-b), c);

Однако вы никогда не сможете заставить PostgreSQL использовать индекс.SQL является декларативным языком, а не императивным.Вы можете побудить сделать это, обновляя статистику таблицы, а также выбрав небольшое количество строк.Если ваш LIMIT слишком большой, PostgreSQL может быть склонен использовать вместо него полное сканирование таблицы.

1 голос
/ 10 апреля 2019

Строго говоря, ваш индекс для (a ASC, b DESC, c ASC) все еще может использоваться, но только на основе ведущего выражения a. См:

Его полезность ограничена, и Postgres будет использовать его, только если предикат только на a достаточно избирателен (менее чем у 5% всех строк есть a >= 10). (Или, возможно, для получения выгоды от сканирования только по индексу, где это возможно.) Но все кортежи индекса, отвечающие только на a, должны быть прочитаны, и вы увидите шаг FILTER в плане запроса, чтобы отбросить неквалифицированные строки - оба добавление дополнительных затрат. Индекс, равный (a), обычно работает лучше, так как он меньше и дешевле в обслуживании.

В прошлом я пытался и не смог в полной мере использовать индекс с неоднородным порядком сортировки (ASC | DESC), который вы отображаете для сравнения значений ROW. Я почти уверен, что невозможно . Подумайте об этом: Postgres сравнивает значения целых строк, которые могут быть как большими, так и меньшими, но не оба одновременно.

Существует обходных путей для типов данных с определенным отрицателем (например, - для числовых типов данных). См. Решение, предоставленное "The Impaler"! Хитрость заключается в том, чтобы инвертировать значения и обернуть их в индекс выражения, чтобы получить равномерный порядок сортировки для всех выражений индекса в конце концов - что в настоящее время является единственным способом получить доступ к полный потенциал сравнения строк. Убедитесь, что оба WHERE условия и ORDER BY соответствуют специальному индексу.

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