Бих пренаписал теста като
IF CASE
WHEN EXISTS (SELECT ...) THEN CASE
WHEN EXISTS (SELECT ...) THEN 1
END
END = 1
Това гарантира късо съединение, както е описано тук, но означава, че трябва да изберете най-евтиния, за да оцените предварително, вместо да го оставите на оптимизатора.
В моите изключително ограничени тестове по-долу следното изглежда е вярно при тестване
1. EXISTS AND EXISTS
Кодът EXISTS AND EXISTS
версията изглежда най-проблематична. Това свързва някои външни полу съединения. В нито един от случаите не е пренаредил реда на тестовете, за да се опита първо да направи по-евтиния (проблем, обсъждан във втората половина на тази публикация в блога). В IF ...
версия нямаше да има никаква разлика, ако имаше, тъй като не беше късо съединение. Когато обаче този комбиниран предикат е поставен в WHERE
клауза планът се променя и тостава късо съединение, така че пренареждането може да е от полза.
/*All tests are testing "If False And False"*/
IF EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
AND EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
PRINT 'Y'
/*
Table 'spt_values'. Scan count 1, logical reads 9
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
IF EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
AND EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
PRINT 'Y'
/*
Table 'spt_monitor'. Scan count 1, logical reads 1
Table 'spt_values'. Scan count 1, logical reads 9
*/
SELECT 1
WHERE EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
AND EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
AND EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_values'. Scan count 1, logical reads 9
*/
Плановете за всичко това изглеждат много сходни. Причината за разликата в поведението между SELECT 1 WHERE ...
версия и IF ...
версията е, че за първата, ако условието е невярно, тогава правилното поведение е да не върне резултат, така че просто свързва OUTER SEMI JOINS
и ако едно е невярно, тогава нула редове се пренасят напред към следващия.
Въпреки това IF
версиявинаги трябва да върне резултат от 1 или нула. Този план използва сонда колона във външните си съединения и задава това на false, ако EXISTS
тестът не е преминал (вместо просто да изхвърли реда). Това означава, че винаги има подаване на 1 ред в следващото присъединяване и то винаги се изпълнява.
CASE
версията има много подобен план, но използва PASSTHRU
предикат, който използва, за да пропусне изпълнението на JOIN, ако предишният THEN
условието не беше изпълнено. Не съм сигурен защо се комбинира AND
s не би използвал същия подход.
2. EXISTS OR EXISTS
EXISTS OR EXISTS
версията използва конкатенация (UNION ALL
) оператор като вътрешен вход към външно полусъединяване. Това подреждане означава, че може да спре да иска редове от вътрешната страна веднага щом се върне първият (т.е. може ефективно да късо съединение). Всичките 4 заявки се оказаха със същия план, където първо беше оценен по-евтиният предикат.
/*All tests are testing "If True Or True"*/
IF EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=1)
OR EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
IF EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
OR EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)= 1)
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)= 1)
OR EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
OR EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
3. Добавяне на ELSE
Хрумна ми да опитам закона на Де Морган за преобразуване на AND
до OR
и вижте дали това има някаква разлика. Преобразуването на първата заявка дава
IF NOT ((NOT EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
OR NOT EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)))
PRINT 'Y'
ELSE
PRINT 'N'
/*
Table 'spt_monitor'. Scan count 1, logical reads 1
Table 'spt_values'. Scan count 1, logical reads 9
*/
Така че това все още няма никаква разлика за поведението при късо съединение. Ако обаче премахнете NOT
и обърнете реда на IF ... ELSE
условия, които сега прави късо съединение!
IF (NOT EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
OR NOT EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1))
PRINT 'N'
ELSE
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/