Оптимизация SQL - план выполнения изменяется на основе ограничительного значения - Почему?

У меня есть таблица ItemValue, полная данных по Серверу SQL 2005, выполняющему в 2000 режим эмуляции, который смотрит что-то как (это - Пользовательская таблица значений):

ID    ItemCode     FieldID   Value
--    ----------   -------   ------
 1    abc123             1   D
 2    abc123             2   287.23
 4    xyz789             1   A
 5    xyz789             2   3782.23
 6    xyz789             3   23
 7    mno456             1   W
 9    mno456             3   45
                                 ... and so on.

FieldID происходит из таблицы ItemField:

ID   FieldNumber   DataFormatID   Description   ...
--   -----------   ------------   -----------
 1             1              1   Weight class
 2             2              4   Cost
 3             3              3   Another made up description
 .             .              x   xxx
 .             .              x   xxx
 .             .              x   xxx
 x             91  (we have 91 user-defined fields)

Поскольку я не могу ВЕРТЕТЬСЯ в 2000 режим, мы застреваем, создавая ужасный запрос с помощью СЛУЧАЕВ и GROUP BY, чтобы заставить данные смотреть, как это должно для некоторых приложений прежней версии, который является:

ItemNumber   Field1   Field2    Field3 .... Field51
----------   ------   -------   ------
    abc123   D        287.23    NULL
    xyz789   A        3782.23   23
    mno456   W        NULL      45

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

SELECT
    iv.ItemNumber,
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1]
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2]
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3]
        ...
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51]
FROM ItemField f
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID
WHERE f.FieldNumber <= 51
GROUP BY iv.ItemNumber

Когда ограничение FieldNumber <= 51, выполнять план идет что-то как:

SELECT <== Computer Scalar <== Stream Aggregate <== Sort (Cost: 70%) <== Hash Match <== (Clustered Index Seek && Table Scan)

и это быстро! Я могу отступить 100,000 + записи приблизительно через секунду, которая удовлетворяет нашим потребностям.

Однако, если у нас было больше UDFs, и я изменяю ограничение на что-либо выше 66 (да, я протестировал их один за другим), или если я удаляю его полностью, я теряю Вид в Плане выполнения, и это заменяется целым набором блоков Параллелизма, которые собирают, повторно делят и распределяют потоки, и вся вещь является медленной (30 секунд даже всего для 1 записи).

FieldNumber имеет кластеризованный, уникальный индекс и является частью составного первичного ключа со столбцом ID (некластерный индекс) в таблице ItemField. Столбцы ID и ItemNumber таблицы ItemValue делают PK, и на столбце ItemNumber существует дополнительный некластерный индекс.

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

И если бы Вы до него..., что Вы сделали бы по-другому? Существует обновление SQL, запланированное в течение пары месяцев с этого времени, но я должен был зафиксировать эту проблему перед этим.

6
задан Dustin Laine 8 June 2011 в 16:55
поделиться

3 ответа

SQL Server достаточно умен, чтобы учитывать ограничения CHECK при оптимизации запросов.

Ваш f.FieldNumber <= 51 оптимизирован, и оптимизатор видит, что все две таблицы должны быть объединены (что лучше всего сделать с помощью HASH JOIN ).

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

Не могли бы вы выложить полные планы по запросам? Просто запустите SET SHOWPLAN_TEXT ON , а затем запросы.

Обновление:

Какова причина этого? Почему изменение моего простого целочисленного ограничения меняет весь план выполнения?

Если под ограничением вы имеете в виду условие WHERE , это, вероятно, другое.

Операции над наборами (это то, что делает SQL ) не имеют единственного наиболее эффективного алгоритма: эффективность каждого алгоритма сильно зависит от распределения данных в наборах.

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

Эффективность первой операции составляет m × const , эффективность последней - n , где m - это номер записи, удовлетворяющей условию, n - общее количество записей в таблице, а const> 1 .

Это означает, что для больших значений м полное сканирование более эффективно.

SQL Server знает об этом и изменяет планы выполнения в соответствии с константами, которые влияют на распределение данных в заданных операциях.

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

Таким образом, изменение целого числа в условии WHERE на самом деле влияет на размер и распределение данных в базовых наборах и заставляет SQL Server пересмотреть алгоритмы, которые лучше всего подходят для работы с наборы такого размера и макета.

4
ответ дан 17 December 2019 в 07:03
поделиться

он заменяется целым набором блоков Parallelism

Попробуйте следующее:

SELECT
    iv.ItemNumber,
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1]
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2]
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3]
        ...
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51]
FROM ItemField f
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID
WHERE f.FieldNumber <= 51
GROUP BY iv.ItemNumber
OPTION (Maxdop 1)

Используя Option (Maxdop 1), это должно предотвратить параллелизм в плане выполнения.

0
ответ дан 17 December 2019 в 07:03
поделиться

В 66 лет вы достигли некоторого внутреннего порога оценки затрат, который решает, что лучше использовать один план по сравнению с другим. Что это за порог и почему это происходит, на самом деле не важно. Обратите внимание, что ваш запрос различается для каждого значения FieldNumber, поскольку вы изменяете не только WHERE: вы также изменяете проецируемые поля псевдо-'pivot '.

Теперь я не знаю всех деталей вашей таблицы и ваших запросов, а также вставки / обновления / удаления / шаблона, но для конкретного запроса, который вы разместили, правильная структура кластеризованного индекса для таблицы ItemValue такова:

CREATE CLUSTERED INDEX  [cdxItemValue] ON ItemValue (FieldID, ItemNumber);

Это Структура исключает необходимость промежуточной сортировки результатов для этого «сводного» запроса.

0
ответ дан 17 December 2019 в 07:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: