Почему в моем случае SQL выбирает неверный индекс? - PullRequest
2 голосов
/ 18 мая 2009

У меня есть таблица с двумя индексами; один представляет собой кластерный индекс из нескольких столбцов на 3 столбца:

(
   symbolid int16,
   bartime int32,
   typeid int8
) 

Второй не кластеризован на

(
   bartime int16
)

Оператор выбора, который я пытаюсь выполнить:

    SELECT symbolID, vTrdBuy
    FROM mvTrdHidUhd 
    WHERE typeID = 1 
    AND barDateTime = 44991 
    AND symbolid in (1010,1020,1030,1040,1050,1060) 

Я запустил этот запрос на sql2008 с помощью редактора sql management studio и, включив фактический план выполнения, обнаружил, что sql использует второй индекс и метод для создания нового индекса для трех столбцов (symbolid, bartime, typeid), но не кластеризованный! !! (Я думаю, что это говорит некластеризованный индекс, поскольку уже есть кластеризованный)

Этот выбор неверен, я снова перезапускаю тот же запрос и заставляю SQL использовать кластерный индекс (используя «с индексом»), и производительность улучшается, как и должно быть.

У меня есть два вопроса: один относится к этому поведению, а второй - к самому запросу

  1. Почему SQL выбирает неправильный индекс и использует тот же индекс
  2. Какой из них следует использовать в условии "where" для лучшей производительности

в символе (1010,1020,1030,1040,1050,1060)

(символ = 1010 или символ = 1020 и т. Д.)

(обозначено между (1010 и 1060))

После тестирования

Я обнаружил, что когда я изменяю условие where с использования IN на использование> = и <= некластеризованный индекс для столбца времени выполнения, дает лучшую производительность, чем кластерный индекс для 3 столбцов. </p>

Так что у меня есть два случая, если ГДЕ использует IN, лучше использовать кластеризованный индекс, если он содержит> = и <=, он использует второй. </p>

Ответы [ 6 ]

3 голосов
/ 03 июня 2009
SELECT  symbolID, vTrdBuy
FROM    mvTrdHidUhd 
WHERE   typeID = 1 
        AND barDateTime = 44991 
        AND symbolid IN (1010,1020,1030,1040,1050,1060)

Это условие не охватывается одним непрерывным диапазоном вашего кластерного индекса.

Эти строки:

1010, 44991, 1
1010, 50000, 1
1020, 44991, 1

придет в порядке в индексе, но ваш запрос выберет первый и третий, пропустив второй.

SQL Server может использовать Clustered Index Seek, если существует ограниченное количество предикатов, как в вашем случае IN. В этом случае он использует ряд диапазонов:

SELECT  symbolID, vTrdBuy
FROM    mvTrdHidUhd 
WHERE   (typeID = 1 
        AND barDateTime = 44991 
        AND symbolid = 1010)
        OR
        (typeID = 1 
        AND barDateTime = 44991 
        AND symbolid = 1010)
        OR …

Но в случае диапазона BETWEEN на symbolid он не может создать такое ограниченное количество предикатов, поэтому он возвращается к менее эффективному Clustered Index Scan (который сканирует на symbolid и просто отфильтровывает неверные результаты ).

В этом случае ваш некластеризованный индекс работает лучше.

Вы можете переписать ваш запрос следующим образом:

SELECT  symbolID, vTrdBuy
FROM    (
        SELECT  DISTINCT symbolid
        FROM    mvTrdHidUhd 
        WHERE   symbolid BETWEEN 1010 AND 1050
        ) s
JOIN    mvTrdHidUhd m
ON      m.symbolid = s.symbolid
        AND m.typeID = 1 
        AND m.barDateTime = 44991

, который также будет использовать Clustered Index Seek на вашем столе, как для построения списка DISTINCT symbolid, так и для присоединения к этому списку.

0 голосов
/ 03 июня 2009

Ваш запрос ссылки четыре столбцы:

  • symbolID
  • vTrdBuy
  • TypeID
  • barDateTime

Хотя кластерный индекс охватывает только три из них

  • symbolID
  • vTrdBuy
  • TypeID
  • barDateTime

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

Следующий столбец в кластерном индексе - vTrdBuy. Это не используется, чтобы помочь ему перейти к нужным строкам.

Глядя на запрос, два столбца очень ограничивают количество строк, которые вы хотите вернуть:

WHERE typeID = 1
AND barDateTime = 44991 

Создание индекса, начинающегося с typeID и barDateTime , действительно может помочь SQL Server перейти к строкам, которые интересуют.

Первый SQL Server может перейти прямо к строкам, которые

typeID = 1. 

Оказавшись там, он может прыгать прямо к барам, где

barDateTime = March 8, 2023

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

Если вы создали индекс:

(
   typeID
   barDateTime
   symbolID
)

все равно может оказаться бесполезным, если запрос возвращает много строк. Чтобы завершить оператор SELECT, SQL Server по-прежнему необходимо значение vTrdBuy . Это необходимо сделать, перепрыгивая таблицу для каждой строк, соответствующих критериям (называемых Поиск закладок ). Если строк слишком много (скажем,> 500), SQL Server просто забудет индекс и просто просканирует всю таблицу - потому что это будет быстрее.

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

CREATE INDEX IX_mvTrdHidUhd_FancyCovering ON mvTrdHidUhd 
(
   typeID, barDateTime, symbolID, vTrdBuy
)

Теперь у вас есть индекс, который содержит все, что хочет SQL Server, в том порядке, в котором он хочет, и вам не нужно связываться с физическим порядком сортировки (то есть кластеризацией) физической таблицы.

0 голосов
/ 19 мая 2009

Вы также можете попробовать индекс покрытия на (symbolid, bartime, typeid, mvTrdBuy)

0 голосов
/ 18 мая 2009

Возможно, порядок столбца индекса влияет на выбор оптимизатором вашего индекса. Вы указываете, что индекс равен (symbolid int16, bartime int32, typeid int8), но symbolid - это наименее различимое значение в предложениях where. Для этого потребуется 6 поисков по индексу для 6 имеющихся у вас значений.

Я бы, вероятно, начал с утверждения между, но наилучшим случаем будет только тестирование с вашими данными, сервером, индексами и т. Д.

Если вы собираетесь создать еще один индекс, попробуйте 2 других порядка для этих столбцов.

И, как уже было отмечено, обновите свою статистику

0 голосов
/ 18 мая 2009

Используйте symbolid BETWEEN 1010 AND 1050, если это возможно. Использование BETWEEN или = или >= или > или < n или <= или их комбинации с AND обычно приводит к лучшей производительности и лучшему выбору индекса, чем использование OR или IN.

0 голосов
/ 18 мая 2009

Обновление статистики в таблице / индексах может привести к выбору правильного индекса

...