с использованием сервера sql 2014; ((SP1-CU3) (KB3094221) 10 октября 2015 г. x64
У меня есть следующий запрос
SELECT * FROM dbo.table1 t1
LEFT JOIN dbo.table2 t2 ON t2.trade_id = t1.tradeNo
LEFT JOIN dbo.table3 t3 ON t3.TradeReportID = t1.tradeNo
order by t1.tradeNo
существуют строки ~ 70k, 35k и 73k в t1, t2 и t3 соответственно.
Когда я опускаю order by
, этот запрос выполняется за 3 секунды с 73k строк.
Как написано, на запрос потребовалось 8,5 минут, чтобы вернуть ~ 50 тыс. строк (я с тех пор его остановил)
Переключение порядка LEFT JOIN
имеет значение:
SELECT * FROM dbo.table1 t1
LEFT JOIN dbo.table3 t3 ON t3.TradeReportID = t1.tradeNo
LEFT JOIN dbo.table2 t2 ON t2.trade_id = t1.tradeNo
order by t1.tradeNo
Теперь это выполняется через 3 секунды.
У меня нет индексов на таблицах. Добавление индексов t1.tradeNo
и t2.trade_id
и t3.TradeReportID
не влияет.
Выполнение запроса только с одним левым соединением (оба сценария) в сочетании с order by
выполняется быстро.
Хорошо для меня, чтобы поменять порядок LEFT JOIN
, но это далеко не объясняет, почему это происходит и по каким сценариям это может случиться снова.
Предполагаемый план exectuion: (медленный)
(информация о восклицательных знаках)
VS
Переключение порядка левого соединения (быстрый):
которые я отмечаю, заметно отличаются, но я не могу их интерпретировать, чтобы объяснить разницу в производительности.
UPDATE
Похоже, добавление результатов предложения order by
в план выполнения с использованием таблицы Spool (lazy spool) против NOT, используя это в быстром запросе.
Если я отключу настольную катушку через DBCC RULEOFF ('BuildSpool');
, это "исправляет" скорость, но в соответствии с этим сообщением это не рекомендуется и не может делать это за каждый запрос
ОБНОВЛЕНИЕ 2
Один из возвращенных столбцов (table3.Text
] имеет тип varchar(max)
). Если это было изменено на nvarchar(512)
, то исходный (медленный) запрос теперь быстр - то есть план выполнения теперь решает не использовать таблицу Spool - также обратите внимание, что даже для типа varchar(max)
значения полей равны NULL для каждой из строк. Теперь это исправление, но я не мудрее
ОБНОВЛЕНИЕ 3
Предупреждения в плане выполнения указаны
Преобразование типов в выражение (CONVERT_IMPLICIT (nvarchar (50), [t2]. [trade_id], 0)) может повлиять на "CardinalityEstimate" в выборе плана запроса,...
t1.tradeNo
- nvarchar(21)
- остальные два являются varchar(50)
- после изменения последних двух до тех же, что и первая проблема исчезает! (оставляя varchar (max), как указано в UPDATE 2 без изменений)
Учитывая, что эта проблема уходит, когда исправлены либо UPDATE 2, либо UPDATE 3, я бы предположил, что комбинация оптимизатора запросов с использованием таблицы temp (table spool) для столбца с неограниченным размером - очень интересна, несмотря на nvarchar(max)
, не имеющий данных.