Сегодня у меня было слишком много совещаний, но я думаю, что мои мозги все еще на месте. Пытаясь улучшить производительность некоторого запроса, я столкнулся со следующей загадкой (имена таблиц и поля перефразированы):
SELECT X.ADId FROM
(
SELECT DISTINCT A.ADId
FROM P WITH (NOLOCK)
INNER JOIN A WITH (NOLOCK) ON (P.ID = A.PId)
INNER JOIN dbo.fn_A(16) AS VD ON (VD.DId = A.ADId)
LEFT JOIN DPR ON (LDID = A.ADId)
WHERE ((A.ADId = 1) OR ((HDId IS NOT NULL) AND (HDId = 1))) AND
(P.PS NOT IN(5,7)) AND (A.ASP IN (2, 3))
) X
WHERE (dbo.fn_B(X.ADId, 16) = 1)
Как вы увидите, содержимое внутреннего запроса в основном не имеет значения. Первоначально весь смысл заключался в том, что я хотел избежать вызова fn_B() для каждой записи, потому что они содержали повторяющиеся значения для ADId, поэтому я сделал SELECT DISTINCT внутренне, а затем отфильтровал отдельные записи. Звучит разумно, верно?
Здесь начинается загадка...
Внутренний запрос возвращает NO RECORDS (для указанных параметров). Если я закомментирую «ГДЕ fn_B() = 1», тогда запрос будет выполнен в нулевое время (и не даст результатов). Если я его снова включу, то запрос занимает 6-10 секунд, снова не возвращая результатов.
Кажется, это превосходит здравый смысл или, по крайней мере, МОЙ здравый смысл в SQL :-) Если внутренний запрос не возвращает данных, то внешние условия никогда не должны оцениваться, верно?
Конечно, я нашел время, чтобы проверить фактические планы выполнения, сохранил их и очень тщательно сравнил. Они на 99% идентичны, в них нет ничего необычного, по крайней мере, так я думаю.
Я повозился с некоторыми CTE, чтобы получить результаты запроса в первом CTE, а затем передать его второму CTE, у которого были некоторые условия, гарантирующие отсутствие фильтрации записей, а затем оценить вызов fn_B() вне всех CTE, но поведение было точно таким же. одинаковый.
Также другие варианты, такие как использование старого запроса (который может вызывать fn_B() несколько раз с одним и тем же значением), имели такое же поведение. Если я уберу условие, я не получу никаких записей в нулевое время. Если поставить обратно, то никаких записей за 10 сек.
Любые идеи?
Спасибо за ваше время :-)
PS1: Я попытался воспроизвести ситуацию в базе данных tempdb с помощью простого запроса, но мне это не удалось. Это происходит только на моих реальных столах. PS2: этот запрос вызывается внутри другой функции, поэтому о помещении результатов во временную таблицу и последующей их фильтрации также не может быть и речи.