Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Условен поток на SQL Server

Бих пренаписал теста като

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
*/


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да промените име на база данни в SQL Server с помощта на T-SQL

  2. Разбиране на SQL Server ALTER TABLE ADD COLUMN Инструкция

  3. Какво представлява схемата в SQL Server и как да създадете/препуснете схема в базата данни на SQL Server - SQL Server / TSQL урок, част 27

  4. Проверете дали съществува временна таблица и изтрийте дали съществува, преди да създадете временна таблица

  5. Visual Studio:ContextSwitchDeadlock