Индексирование простого запроса в огромной базе данных - PullRequest
5 голосов
/ 31 января 2011

Я получил таблицу, содержащую почти 850 000 000 строк.

В таблице есть следующие поля:

[ID] [bigint] IDENTITY(1,1) NOT NULL,
[D1] [int] NOT NULL,
[D2] [int] NOT NULL,
[D3] [int] NOT NULL,
[D4] [int] NOT NULL,
[D5] [int] NOT NULL,
[D6] [int] NOT NULL,
[D7] [int] NOT NULL,
[D8] [int] NOT NULL,
[D9] [int] NOT NULL,
[A] [int] NOT NULL,
[Hb] [bit] NOT NULL,

Все мои запросы для этой таблицы практически одинаковы -

Select [D1-D9], [A] Where [Hb] = 0 AND [D1] <> x AND [D2] <> y AND [D3] = z,

и т. Д. *

Каждый запрос ВСЕГДА запрашивает ВСЕ поля [D1-D9] и всегда запрашивает [Hb] = 0

Пример длязапрос:

SELECT [D1], [D2], [D3], [D4], [D5], [D6],[D7], [D8],[D9], [A] 
  from [myTable] 
 WHERE [D1] <> 8 AND [D2] <> 2 AND [D3] <> 5 AND [D4] = 8 AND [D5] = 2 
   AND [D6] = 5 AND [D7] = 5 AND [D8] = 3 AND [D9] = 4 AND [A] = 0 AND [Hb] = 0

Как мне проиндексировать эту таблицу для самых быстрых результатов?

Большое спасибо

Ответы [ 6 ]

4 голосов
/ 31 января 2011

Лучшее, что вы можете сделать, - это сначала выполнить проверку индекса на предмет равенства, а затем поиск остаточного неравенства. То есть = до <>.

Перестановка предложения WHERE:

WHERE
--Equality
D4 = 8 AND D5 = 2 AND D6 = 5 AND D7 = 5 AND D8 = 3 AND D9 = 4 AND A = 0 
--in the middle    
AND Hb = 0
--Non-Equality
D1 <> 8 AND D2 <> 2 AND D3 <> 5

Итак, первый черновик такой:

CREATE .. INDEX ... ON (D4, D5, D6, D7, D8, D9, A, Hb, D1, D2, D3)

Порядок от D4 до D9 должен основываться на селективности. Сначала самые высокие номера. Hb всегда должен идти последним в столбцах равенства, потому что это бит

SELECT
   COUNT(DISTINCT D4) AS D4COunt,
   COUNT(DISTINCT D5) AS D5COunt,
   COUNT(DISTINCT D6) AS D6COunt,
   COUNT(DISTINCT D7) AS D7COunt,
   COUNT(DISTINCT D8) AS D8COunt,
   COUNT(DISTINCT D9) AS D9COunt,
   COUNT(DISTINCT A) AS ACOunt
FROM
    Mytable

Наконец, это может быть кластеризовано или не кластеризовано. Если у вас нет других индексов или нет FK, я бы подумал сделать это кластерным PK. В противном случае просто создайте кластерный суррогатный ключ и сделайте этот индекс НЕКЛЮЧЕННЫМ

Edit:

Статья, которая (надеюсь) объясняет, почему порядок столбцов имеет значение для индексов с несколькими столбцами: Предикаты поиска Крейга Фридмана . И его сканирует и ищет тоже

Edit2:

Я спросил, если = до <> находятся в тех же столбцах: появилось "да". Комментарий ОП к этому ответу говорит «нет», поэтому все, что я здесь сказал, бессмысленно

Ответ от предложенных Damien_The_Unbeliever пересечений индексов, чтобы попытаться обойти это сочетание равенства / неравенства.

2 голосов
/ 31 января 2011

Расширение ответа @ gbn .

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

Однако дляВ каждом столбце вы должны решить, хотите ли вы, чтобы он был ключевым столбцом или включенным столбцом в индексе.

Для этого выполните этот запрос:

SELECT  SUM(CASE D1 WHEN 8 THEN 0 ELSE 1 END) / COUNT(*) AS D1Card,
        SUM(CASE D2 WHEN 2 THEN 0 ELSE 1 END) / COUNT(*) / COUNT(DISTINCT D2) AS D2Card,
        SUM(CASE D3 WHEN 5 THEN 0 ELSE 1 END) / COUNT(*) / COUNT(DISTINCT D3) AS D3Card,
        SUM(CASE d4 WHEN 8 THEN 1 ELSE 0 END) / COUNT(DISTINCT D4) AS D4Card,
        SUM(CASE d5 WHEN 2 THEN 1 ELSE 0 END) / COUNT(DISTINCT D5) AS D5Card,
        SUM(CASE d6 WHEN 5 THEN 1 ELSE 0 END) / COUNT(DISTINCT D6) AS D6Card,
        SUM(CASE d7 WHEN 5 THEN 1 ELSE 0 END) / COUNT(DISTINCT D7) AS D7Card,
        SUM(CASE d8 WHEN 3 THEN 1 ELSE 0 END) / COUNT(DISTINCT D8) AS D8Card,
        SUM(CASE d9 WHEN 4 THEN 1 ELSE 0 END) / COUNT(DISTINCT D9) AS D9Card,
        SUM(CASE a WHEN 0 THEN 1 ELSE 0 END) / COUNT(DISTINCT A) AS ACard,
        SUM(CASE Hb WHEN 0 THEN 1 ELSE 0 END) / COUNT(DISTINCT Hb) AS HbCard
FROM    Mytable

Вы должны создать списокнаименее селективных столбцов (столбцов с самыми высокими значениями *Card), которые (вместе) составляют более 25% ваших записей.

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

Column  Selectivity  Cumulative selectivity
D4      0.96         0.96
D8      0.87         0.84
D9      0.85         0.70
D7      0.72         0.51
D6      0.65         0.33 -- here
D5      0.20         0.07
A       0.02         0.00
Hb      0.01         0.00

Это означает, что условия для столбцов d4, d8, d9, d7, d6 вместе соответствуют примерно 33% ваших записей.

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

CREATE INDEX ix_mytable_filter ON (Hb, A, D5) INLCUDE (D1, D2, D3, D4, D6, D7, D8, D9)

Столбцы с фильтром неравенства всегда идут в раздел INCLUDE.

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

Может также случиться, что такие условия, как [D1] <> 8, включают в себя магические числа, и существует несколько записей длякоторого выполняется это условие.

В этом случае вы можете добавить вычисляемый столбец в определение вашей таблицы:

ALTER TABLE mytable ADD d1_ne_8 AS CASE D1 WHEN 8 THEN 0 ELSE 1 END

и добавить это выражение в индекс (с учетом приведенных выше правил).

Если вы сделаете это, вам придется использовать d1_ne_8 = 1 вместо d1 <> 8.

2 голосов
/ 31 января 2011

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

0 голосов
/ 31 января 2011

По сути, вы должны создать составной индекс, начинающийся с колонки с проверкой на равенство. Итак, в вашем случае естественно использовать [Hb] в качестве первого компонента, поскольку вы заявили, что [Hb] будет проверяться на равенство. Следующими элементами индекса являются [D *], за которыми следует [A]

create index IXC_MyTable1 on Mytable(Hb, D1, D2, D3, D4, D5, D6, D7, D8, D9, A)

Во-вторых, вы можете использовать частичное индексирование и позволить БД выполнить быстрое сканирование индекса (CMIIW) таблицы, чтобы проверить другие значения. В этом случае вы должны включить Id в качестве последнего элемента индекса. Например:

create index IXC_MyTable__D123 on Mytable(Hb, D1, D2, D3, Id)
create index IXC_MyTable__D456 on Mytable(Hb, D4, D5, D6, Id)
create index IXC_MyTable__D789 on Mytable(Hb, D7, D8, D9, Id)

Запрос будет использовать индекс IXC_MyTable__D123, когда он использует проверки на равенство для Hb, D1, D2 и D3; и т. д.

0 голосов
/ 31 января 2011

Прежде всего запросы с условиями типа «где X <> 8» могут сделать любой индекс бесполезным (это может зависеть от вашего фактического механизма БД.

Безопаснее изменить это с

SELECT [D1], [D2], [D3], [D4], [D5], [D6],[D7], [D8],[D9], [A] 
  from [myTable] 
 WHERE [D1] <> 8 AND [D2] <> 2 AND [D3] <> 5 AND [D4] = 8 AND [D5] = 2 
   AND [D6] = 5 AND [D7] = 5 AND [D8] = 3 AND [D9] = 4 AND [A] = 0 AND [Hb] = 0

на что-то вроде этого:

SELECT [D1], [D2], [D3], [D4], [D5], [D6],[D7], [D8],[D9], [A] 
  from [myTable] 
 WHERE ([D1] < 8 or [D1] > 8) 
       AND ([D2] < 2 or [D2] > 2) 
       AND ([D3] < 5 or [D3] > 5) 
       AND [D4] = 8 AND [D5] = 2  AND [D6] = 5 AND [D7] = 5 AND [D8] = 3 
       AND [D9] = 4 AND [A] = 0 AND [Hb] = 0
0 голосов
/ 31 января 2011

Если ваш алгоритм является детерминированным (то есть A = f (d1, d2, d3 ... d9)), то ваши столбцы D в сочетании с Hb составляют ключ.Попробуйте создать кластерный составной индекс для всех столбцов D и Hb, разделив его на Hb, чтобы немного увеличить скорость.Вы можете также рассмотреть возможность удаления поля ID.

РЕДАКТИРОВАТЬ: Только что понял, что я пропустил условия <>.Как уже упоминали другие, это усложняет ситуацию.Здесь вы действительно хотите использовать растровый индекс, но в AFAIK SQL Server их нет.Вероятно, вам нужно будет полагаться на отдельные индексы столбцов, которые хорошо играют вместе.

...