Дополнение: SQL Server 2012 демонстрирует некоторое улучшение производительности в этой области, но, похоже, не решает конкретных проблем, указанных ниже. Очевидно, это должно быть исправлено в следующей основной версии. после
SQL Server 2012!
Ваш план показывает, что отдельные вставки используют параметризованные процедуры (возможно, автоматически параметризованные), поэтому время их синтаксического анализа / компиляции должно быть минимальным.
Я подумал, что займусь этим немного подробнее, поэтому настройте цикл ( скрипт ) и попытался количество VALUES
предложений и записать время компиляции.
Затем я разделил время компиляции на количество строк, чтобы получить среднее время компиляции для каждого предложения. Результаты ниже
Вплоть до 250 VALUES
предложений время компиляции / количество предложений имеет небольшую тенденцию к увеличению, но не слишком драматичную.
Но затем происходит внезапное изменение.
Этот раздел данных показан ниже.
+------+----------------+-------------+---------------+---------------+
| Rows | CachedPlanSize | CompileTime | CompileMemory | Duration/Rows |
+------+----------------+-------------+---------------+---------------+
| 245 | 528 | 41 | 2400 | 0.167346939 |
| 246 | 528 | 40 | 2416 | 0.162601626 |
| 247 | 528 | 38 | 2416 | 0.153846154 |
| 248 | 528 | 39 | 2432 | 0.157258065 |
| 249 | 528 | 39 | 2432 | 0.156626506 |
| 250 | 528 | 40 | 2448 | 0.16 |
| 251 | 400 | 273 | 3488 | 1.087649402 |
| 252 | 400 | 274 | 3496 | 1.087301587 |
| 253 | 400 | 282 | 3520 | 1.114624506 |
| 254 | 408 | 279 | 3544 | 1.098425197 |
| 255 | 408 | 290 | 3552 | 1.137254902 |
+------+----------------+-------------+---------------+---------------+
Размер кэшированного плана, который увеличивался линейно, внезапно падает, но CompileTime увеличивается в 7 раз, а CompileMemory - вверх. Это точка отсечения между автоматически параметризованным планом (с 1000 параметрами) и непараметризованным планом. После этого он, кажется, становится линейно менее эффективным (с точки зрения количества предложений значений, обрабатываемых за заданное время).
Не уверен, почему это должно быть. Предположительно, когда он составляет план для конкретных буквальных значений, он должен выполнять некоторые действия, которые не масштабируются линейно (например, сортировка).
Похоже, что это не влияет на размер кешированного плана запроса, когда я пробовал запрос, состоящий полностью из повторяющихся строк, и не влияет на порядок вывода таблицы констант (и когда вы вставляете в кучу время, потраченное на сортировку в любом случае было бы бессмысленно, даже если бы это было).
Более того, если в таблицу добавляется кластеризованный индекс, план по-прежнему показывает явный шаг сортировки, поэтому во время компиляции не выполняется сортировка, чтобы избежать сортировки во время выполнения.
Я попытался посмотреть на это в отладчике, но общедоступные символы для моей версии SQL Server 2008, похоже, недоступны, поэтому вместо этого мне пришлось посмотреть на эквивалент UNION ALL
конструкцию в SQL Server 2005.
Типичная трассировка стека ниже
sqlservr.exe!FastDBCSToUnicode() + 0xac bytes
sqlservr.exe!nls_sqlhilo() + 0x35 bytes
sqlservr.exe!CXVariant::CmpCompareStr() + 0x2b bytes
sqlservr.exe!CXVariantPerformCompare<167,167>::Compare() + 0x18 bytes
sqlservr.exe!CXVariant::CmpCompare() + 0x11f67d bytes
sqlservr.exe!CConstraintItvl::PcnstrItvlUnion() + 0xe2 bytes
sqlservr.exe!CConstraintProp::PcnstrUnion() + 0x35e bytes
sqlservr.exe!CLogOp_BaseSetOp::PcnstrDerive() + 0x11a bytes
sqlservr.exe!CLogOpArg::PcnstrDeriveHandler() + 0x18f bytes
sqlservr.exe!CLogOpArg::DeriveGroupProperties() + 0xa9 bytes
sqlservr.exe!COpArg::DeriveNormalizedGroupProperties() + 0x40 bytes
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x18a bytes
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes
sqlservr.exe!CQuery::PqoBuild() + 0x3cb bytes
sqlservr.exe!CStmtQuery::InitQuery() + 0x167 bytes
sqlservr.exe!CStmtDML::InitNormal() + 0xf0 bytes
sqlservr.exe!CStmtDML::Init() + 0x1b bytes
sqlservr.exe!CCompPlan::FCompileStep() + 0x176 bytes
sqlservr.exe!CSQLSource::FCompile() + 0x741 bytes
sqlservr.exe!CSQLSource::FCompWrapper() + 0x922be bytes
sqlservr.exe!CSQLSource::Transform() + 0x120431 bytes
sqlservr.exe!CSQLSource::Compile() + 0x2ff bytes
Таким образом, если отбросить имена в трассировке стека, кажется, что на сравнение строк уходит много времени.
В этой статье базы знаний указано, что DeriveNormalizedGroupProperties
это связано с тем, что раньше называлось нормализацией. этапом обработки запросов.
Этот этап теперь называется привязкой или алгебраизацией, и он берет выходные данные дерева синтаксического анализа выражений с предыдущего этапа синтаксического анализа и выводит дерево алгебризованных выражений (дерево обработчика запросов) для перехода к оптимизации (в данном случае - тривиальной оптимизации плана) [ref] .
Я попробовал еще один эксперимент ( сценарий ), который заключался в повторном запуске исходного теста, но с рассмотрением трех разных случаев.
- Имя и фамилия Строки длиной 10 символов без дубликатов.
- Имя и фамилия Строки длиной 50 символов без дубликатов.
- Имя и фамилия Строки длиной 10 символов со всеми дубликатами.
Хорошо видно, что чем длиннее струны, тем хуже получается, и, наоборот, чем больше дубликатов, тем лучше. Как упоминалось ранее, дубликаты не влияют на размер кэшированного плана, поэтому я предполагаю, что при построении самого алгебризованного дерева выражений должен существовать процесс идентификации дубликатов.
редактировать
Одно место, где используется эта информация, показано здесь @Lieven.
SELECT *
FROM (VALUES ('Lieven1', 1),
('Lieven2', 2),
('Lieven3', 3))Test (name, ID)
ORDER BY name, 1/ (ID - ID)
Поскольку во время компиляции он может определить, что Name
столбец не имеет дубликатов, он пропускает упорядочение по вторичному 1/ (ID - ID)
выражению во время выполнения (сортировка в плане имеет только один ORDER BY
столбец), и ошибка деления на ноль не возникает. Если в таблицу добавляются дубликаты, тогда оператор сортировки показывает два столбца в порядке столбцов, и возникает ожидаемая ошибка.