sql >> Databáze >  >> RDS >> Sqlserver

SQL Server podmíněný tok

Test bych přepsal jako

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

Tím je zaručeno zkratování, jak je popsáno zde, ale znamená to, že musíte vybrat ten nejlevnější, který chcete vyhodnotit předem, a ne to nechat na optimalizátorovi.

V mých extrémně omezených testech níže se zdálo, že následující platí při testování

1. EXISTS AND EXISTS

EXISTS AND EXISTS verze se zdá nejproblematičtější. Toto spojuje dohromady některé vnější polospojky. V žádném z případů nezměnil pořadí testů tak, aby se nejprve pokusil provést ten levnější (problém diskutovaný v druhé polovině tohoto blogového příspěvku). V IF ... verze by to nedělalo žádný rozdíl, kdyby to mělo, protože nezkratovalo. Když je však tento kombinovaný predikát vložen do WHERE klauzule, že se plán změní, a to dělá zkrat, takže přeskupení mohlo být prospěšné.

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

*/

Plány pro všechny se zdají být velmi podobné. Důvod rozdílu v chování mezi SELECT 1 WHERE ... verzi a IF ... verze je taková, že pokud je podmínka nepravdivá, u první verze je správným chováním nevracet žádný výsledek, takže pouze zřetězí OUTER SEMI JOINS a pokud je jedna nepravda, pak se nula řádků přenese na další.

Nicméně IF verze vždy potřebuje vrátit výsledek 1 nebo nula. Tento plán používá ve svých vnějších spojích sloupec sondy a nastaví toto na hodnotu false, pokud EXISTS test neprojde (spíše než jednoduše zahodit řádek). To znamená, že do dalšího spojení je vždy vložen 1 řádek a vždy se provede.

CASE verze má velmi podobný plán, ale používá PASSTHRU predikát, který používá k přeskočení provedení JOIN, pokud je předchozí THEN podmínka splněna nebyla. Nejsem si jistý, proč kombinovat AND s by nepoužil stejný přístup.

2. EXISTS OR EXISTS

EXISTS OR EXISTS verze používala zřetězení (UNION ALL ) operátor jako vnitřní vstup do vnějšího polovičního spojení. Toto uspořádání znamená, že může přestat vyžadovat řádky z vnitřní strany, jakmile se vrátí první (tj. může účinně zkratovat). Všechny 4 dotazy skončily se stejným plánem, kde byl jako první vyhodnocen levnější predikát.

/*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. Přidání ELSE

Napadlo mě zkusit De Morganův zákon převést AND na OR a uvidíme, jestli se to nějak změnilo. Převod prvního dotazu dává

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

Takže to stále nemění žádný vliv na chování při zkratu. Pokud však odstraníte NOT a obrátit pořadí IF ... ELSE podmínky, které nyní dělá zkrat!

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. Možnosti ladění výkonu Azure SQL Database

  2. Chyba systému Android:Tuto operaci nelze provést, protože fond připojení byl uzavřen

  3. SQL Server:Zjistěte výchozí hodnotu sloupce pomocí dotazu

  4. Získávání ORA-03115:Nepodporovaný datový typ sítě nebo chyba reprezentace při načítání pole varchar z anonymního pl/sql