Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Przepływ warunkowy programu SQL Server

Przepisałbym test jako

IF CASE
     WHEN EXISTS (SELECT ...) THEN CASE
                                   WHEN EXISTS (SELECT ...) THEN 1
                                 END
   END = 1  

Gwarantuje to zwarcie, jak opisano tutaj, ale oznacza to, że musisz wybrać najtańszy do oceny z góry, zamiast pozostawiać to optymalizatorowi.

W moich niezwykle ograniczonych testach poniżej, podczas testowania sprawdziły się następujące elementy

1. EXISTS AND EXISTS

EXISTS AND EXISTS wersja wydaje się najbardziej problematyczna. To łączy ze sobą niektóre zewnętrzne półłącza. W żadnym z przypadków nie zmieniła kolejności testów, aby najpierw wykonać tańszy (kwestia omówiona w drugiej połowie tego wpisu na blogu). W IF ... wersja nie zrobiłaby żadnej różnicy, gdyby miała, ponieważ nie ma zwarcia. Jednak gdy ten połączony predykat zostanie umieszczony w WHERE klauzula o zmianach w planie i tak zwarcie, aby zmiana układu mogła być korzystna.

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

*/

Plany na to wszystko wydają się bardzo podobne. Powód różnicy w zachowaniu między SELECT 1 WHERE ... wersja i IF ... wersja jest taka, że ​​w przypadku pierwszego, jeśli warunek jest fałszywy, prawidłowym zachowaniem jest nie zwracanie żadnego wyniku, więc po prostu łączy OUTER SEMI JOINS a jeśli jeden jest fałszywy, to zero wierszy jest przenoszone do następnego.

Jednak IF wersja zawsze musi zwrócić wynik 1 lub zero. Ten plan używa kolumny sondującej w swoich zewnętrznych sprzężeniach i ustawia ją na wartość false, jeśli EXISTS test nie został zaliczony (zamiast po prostu odrzucić wiersz). Oznacza to, że do następnego złączenia zawsze podawany jest jeden wiersz i zawsze jest on wykonywany.

CASE wersja ma bardzo podobny plan, ale używa PASSTHRU predykat, którego używa do pominięcia wykonania JOIN, jeśli poprzedni THEN warunek nie został spełniony. Nie jestem pewien, dlaczego łączymy AND s nie zastosowaliby tego samego podejścia.

2. EXISTS OR EXISTS

EXISTS OR EXISTS wersja użyła konkatenacji (UNION ALL ) jako wewnętrzne dane wejściowe do zewnętrznego sprzężenia częściowego. Ten układ oznacza, że ​​może przestać żądać wierszy od wewnętrznej strony, gdy tylko pierwszy z nich zostanie zwrócony (tj. może skutecznie spowodować zwarcie). Wszystkie 4 zapytania zakończyły się tym samym planem, w którym najpierw oceniano tańszy predykat.

/*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. Dodawanie ELSE

Przyszło mi do głowy, aby wypróbować prawo De Morgana, aby przekonwertować AND do OR i zobacz, czy to coś zmieniło. Konwersja pierwszego zapytania daje

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

Więc to nadal nie ma żadnego wpływu na zachowanie zwarciowe. Jeśli jednak usuniesz NOT i odwróć kolejność IF ... ELSE warunkuje to teraz tak zwarcie!

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:klauzula IF w klauzuli WHERE

  2. Automatyczne usuwanie zapomnianych transakcji w MS SQL Server

  3. Jak zmienić domyślny profil poczty bazy danych dla użytkownika w programie SQL Server (T-SQL)

  4. Jak uzyskać dane wyjściowe eksportu w rzeczywistym formacie CSV w SQL Server Management Studio?

  5. Znaki ucieczki w wyszukiwaniu pełnotekstowym programu SQL Server?