Есть ли лучший способ индексировать несколько столбцов, чем создание индекса для каждой перестановки?

Предположим, у меня есть таблица базы данных со столбцами a, b и c. Я планирую делать запросы по всем трем столбцам, но я не уверен, какие именно столбцы я запрашиваю. В таблице достаточно строк, что индекс чрезвычайно ускоряет поиск, но неправильно делать все перестановки возможных индексов (например, это):

a
b
c
a, b
a, c
b, c
a, b, c

есть ли лучший способ справиться с этой проблемой? (Очень возможно, что я буду просто хорошо индексировать A, b, c в одиночку, так как это сократит вниз по количеству строк быстро, но мне интересно, есть ли лучший способ.)

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

5 ответов


в MS SQL индекс "a, b, c" будет охватывать вас для сценариев "a"; "a, b"; и "a, b, c". Таким образом, вам понадобятся только следующие индексы:

a, b, c
b, c
c

Не уверен, работает ли MySQL таким же образом, но я бы предположил это.


использовать индексы для всех возможных условий равенства на N колонки, вам понадобится C([N/2], N) индексы, что составляет N! / ([N/2]! * (N - [N/2])!)

см. эту статью в моем блоге для подробных объяснений:

вы также можете прочитать строгую математическую доказательство российский математик Egor Timoshenko (обновление: сейчас в Английский.)

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

индекс слияния

если колонки col1, col2 и col3 являются выборочными, то этот запрос

SELECT  *
FROM    mytable
WHERE   col1 = :value1
        AND col2 = :value2
        AND col3 = :value3

можно использовать три отдельных индекса на col1, col2 и col3 выберите ROWID, которые соответствуют каждому условию отдельно, и они находят их пересечение, например в:

SELECT  *
FROM    (
        SELECT  rowid
        FROM    mytable
        WHERE   col1 = :value1
        INTERSECT
        SELECT  rowid
        FROM    mytable
        WHERE   col2 = :value2
        INTERSECT
        SELECT  rowid
        FROM    mytable
        WHERE   col3 = :value3
        ) mo
JOIN    mytable mi
ON      mi.rowid = mo.rowid

растровые индексации

PostgreSQL может создавать временные растровые индексы в памяти прямо во время запроса.

растровый индекс - это довольно компактный непрерывный битовый массив.

каждый бит в массиве говорит, что corresponging tid должно быть выбрано из таблицы.

такой индекс может занять но 128M временного хранения для таблиц с 1G строк.

в следующий запрос:

SELECT  *
FROM    mytable
WHERE   col1 = :value1
        AND col2 = :value2
        AND col3 = :value3

сначала выделит нулевое растровое изображение, достаточно большое, чтобы покрыть все возможные tidв таблице (это достаточно большой, чтобы взять все tidС (0, 0) до последнего tid, не принимая отсутствует tidС учетом).

тогда он будет искать первый индекс, устанавливая биты в 1 если они удовлетворяют первому условию.

тогда он будет сканировать второй индекс,AND ' ing биты, которые удовлетворяют второй условие с 1. Это уйдет 1 только для тех битов, которые удовлетворяют обоим условиям.

то же самое для третьего индекса.

наконец, он просто выберет строки с tidсоответствует набору битов.

на tid'S будет извлечена последовательно, так что это очень эффективно.


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

Да, вы можете использовать индексы с несколькими столбцами. Что-то вроде

CREATE TABLE temp (
    id         INT NOT NULL,
    a          INT NULL,
    b          INT NULL,
    c          INT NULL,
    PRIMARY KEY (id),
    INDEX ind1 (a,b,c),
    INDEX ind2 (a,b)
);

этот тип индекса, т. е. ind1, безусловно, поможет вам в таких запросах, как

SELECT * FROM temp WHERE a=2 AND b=3 AND c=4;

аналогично, ind2 поможет вам в таких запросах, как

SELECT * FROM temp WHERE a=2 AND b=3;

но эти индексы не будут использоваться, если запрос-это что-то как

SELECT * FROM temp WHERE a=2 OR b=3 OR c=4;

здесь вам понадобятся отдельные индексы на a, b и c.

поэтому вместо того, чтобы иметь так много индексов,я бы согласился с тем,что сказал Джон, Т. е. иметь индексы на a, b, c, и если вы чувствуете, что ваша рабочая нагрузка охватывает больше многоколоночных запросов, то вы можете переключиться на многоколоночные индексы.

ура


учитывая, что ваши столбцы на самом деле город, штат и почтовый индекс, Я бы предложил только следующие индексы:

индекс (ZipCode)

Если я прав, почтовые индексы не дублируются по США, поэтому бессмысленно добавлять информацию о городе или штате в индекс, потому что они будут одинаковыми для всех почтовых индексов. Е. Г., 90210-это всегда Лос-Анджелес, Калифорния.

индекс (город (5)) или индекс (город (5)), State)

это всего лишь указатель на первые пять букв названия города. Во многих случаях это будет достаточно специфично, чтобы иметь State indexed не обеспечит никакой полезной фильтрации. Например, "Los A" почти наверняка будет записями из Лос-Анджелеса, Калифорния. Возможно, в США есть еще один маленький город, начинающийся с "Лос-А", но там будет так мало записей, что не стоит загромождать индекс государственными данными. С другой стороны, некоторые названия городов появляются во многих Штаты (Спрингфилд приходит на ум), поэтому в этих случаях лучше также индексировать состояние. Вам нужно будет выяснить для себя, какой индекс больше всего подходит для вашего набора данных. Если сомневаетесь, я бы пошел со вторым индексом (город и штат).

(государство sort_field)

государство - довольно широкий индекс (вполне возможно, что только NY и CA будут иметь 30% записей). Если вы планируете отображать эту информацию пользователю, скажем, 30 записей за раз, тогда у вас будет запрос, заканчивающийся на

... WHERE STATE = "NY"
ORDER BY <sort_field>
LIMIT <number>, 30

сделать это запрос эффективен, вам нужно включить столбец сортировки в индекс состояния. Поэтому, если вы показываете страницы, упорядоченные по фамилии (при условии, что у вас есть этот столбец), вы бы использовали индекс (состояние, фамилия(3)), в противном случае MySQL должен сортировать все из записей "NY", прежде чем он сможет дать вам 30, которые вы хотите.


Это зависит от вашего SQL-запроса.

индекс (a, b, c) отличается от индекс (b, c, a) или индекс (a, c, b)