2017-02-10 6 views
2

У меня есть заявление, которое занимает 10 секунд для выполнения на 10k элементов в таблице X:Почему мой некоррелированный подзапрос так медленно?

Version 1

SELECT * 
FROM X 
WHERE pk = 77843 
    AND (a IS NULL OR a NOT IN (SELECT DISTINCT(b) 
           FROM X 
           WHERE pk = 77843 
           AND l IS NOT NULL)) 

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

Версия 2:

Теперь, если я извлечь подзапрос и выполнить вычисление заранее запрос выполняется в < 1s.

DECLARE @listOfb table (id int) 

INSERT INTO @listOfb(id) 
    (SELECT DISTINCT(b) as Numbers 
    FROM X 
    WHERE pk = 77843 
     AND l IS NOT NULL) 

SELECT * 
FROM X 
WHERE pk = 77843 
    AND (a IS NULL OR a NOT IN (SELECT * FROM @listOfb)) 

Так почему версия 2 намного быстрее, чем версия 1?

Update

Я добавил (то, что я думаю, что называется) план выполнения версии 1: Запрос удаления около 10k строк.

enter image description here

+3

Вы просматривали ** планы выполнения ** для двух запросов? –

+0

'DISTINCT' не является функцией (в столбце), она является частью' SELECT DISTINCT' и работает со всеми выбранными строками. Удалите эти лишние круглые скобки, чтобы сделать вещи более ясными! 'SELECT DISTINCT (a), b ...' лучше написано как 'SELECT DISTINCT a, b ...', но также может быть записано как 'SELECT DISTINCT a, (b) ...' ... – jarlh

+1

Однако , не нужно делать SELECT DISTINCT здесь ... – jarlh

ответ

1

Попробуйте с общим выражением таблицы и UNION:

;WITH CTE 
AS 
(
    SELECT * 
    FROM X 
    WHERE pk = 77843 
) 
SELECT * 
FROM CTE 
WHERE a IS NULL 
UNION ALL 
SELECT * 
FROM CTE C1 
WHERE a IS NOT NULL AND 
     NOT EXISTS (SELECT * FROM CTE C2 WHERE C1.a = C2.b AND l IS NULL) 
+1

Нет необходимости в первом выборе, 'NOT EXISTS' возвращает строки с NULL в' a' в любом случае. – dnoeth

1

Вы должны иметь взглянуть на этот article

у меня нет версии одного и того же сервера, но что я будет пытаться разблокировать «от x» в основном запросе, а также в подзапросе.

SELECT * 
FROM X (NOLOCK) 
WHERE pk = 77843 

Из моего небольшого опыта, зависит от размера таблицы и индексы на ней, я обнаружил, иногда разницу производительности при запросе в два раза один и тот же стол (особенно с тем же условием «рк = 77843» и/или обновлением/удаление операций).

О своем последнем комментарии. Я не вижу в плане выполнения, когда суб-запрос выполняется несколько раз. На мой взгляд, первый index_seek блокирует столбец pk [основной запрос] и при приеме второго index_seek [подзапроса] в том же столбце (думаю, я не могу видеть все детали с вашего снимка экрана) которые вызывают проблемы с производительностью.

Но в этом случае вы получите лучшую производительность при выполнении этих двух запросов (с почти одинаковыми условиями) отдельно.