Как уже указывалось в комментариях, похоже, вам нужно обновить статистику.
По оценкам , число строк , выходящий из соединения между location
и testruns
является сильно отличаются между этими двумя планами.
Присоединиться к плану оценки: 1
Оценка плана подзапроса: 8 748
Фактическое количество строк, выходящих из объединения, составляет 14 276.
Конечно, нет абсолютно никакого интуитивного смысла в том, что версия соединения должна оценивать 3 строки из location
и производить единственную объединенную строку, тогда как подзапрос оценивает, что одна из этих строк произведет 8 748 из того же соединения, но тем не менее я смог воспроизвести это.
Похоже, это происходит, если при создании статистики между гистограммами нет пересечения. Версия соединения предполагает одну строку. И поиск единственного равенства подзапроса предполагает те же оценочные строки, что и поиск равенства неизвестной переменной.
Кардинальность теструнов есть 26244
. Предполагая, что он заполнен тремя различными идентификаторами местоположения, тогда следующий запрос оценивает, что 8,748
строки будут возвращены ( 26244/3
)
declare @i int
SELECT *
FROM testruns AS tr
WHERE tr.location_id = @i
Учитывая, что таблица locations
содержит только 3 строки, легко (если мы предполагаем отсутствие внешних ключей) придумать ситуацию, когда создается статистика, а затем данные изменяются таким образом, что существенно влияет на фактическое количество возвращаемых строк, но этого недостаточно для отключить автоматическое обновление статистики и перекомпилировать порог.
Поскольку SQL Server получает количество строк, выходящих из этого объединения, это неправильно, все остальные оценки строк в плане объединения существенно недооцениваются. Помимо того, что это означает, что вы получаете последовательный план, запрос также получает недостаточный объем памяти, и разливаются соединения сортировки и хеша tempdb
.
Один из возможных сценариев, который воспроизводит фактические и оценочные строки, показанные в вашем плане, приведен ниже.
CREATE TABLE location
(
id INT CONSTRAINT locationpk PRIMARY KEY,
location VARCHAR(MAX) /*From the separate filter think you are using max?*/
)
/*Temporary ids these will be updated later*/
INSERT INTO location
VALUES (101, 'Coventry'),
(102, 'Nottingham'),
(103, 'Derby')
CREATE TABLE testruns
(
location_id INT
)
CREATE CLUSTERED INDEX IX ON testruns(location_id)
/*Add in 26244 rows of data split over three ids*/
INSERT INTO testruns
SELECT TOP (5984) 1
FROM master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (5984) 2
FROM master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (14276) 3
FROM master..spt_values v1, master..spt_values v2
/*Create statistics. The location_id histograms don't intersect at all*/
UPDATE STATISTICS location(locationpk) WITH FULLSCAN;
UPDATE STATISTICS testruns(IX) WITH FULLSCAN;
/* UPDATE location.id. Three row update is below recompile threshold*/
UPDATE location
SET id = id - 100
Затем выполнение следующих запросов дает такое же расчетное и фактическое расхождение
SELECT *
FROM testruns AS tr
WHERE tr.location_id = (SELECT id
FROM location
WHERE location = 'Derby')
SELECT *
FROM testruns AS tr
JOIN location loc
ON tr.location_id = loc.id
WHERE loc.location = ( 'Derby' )