sql >> Databasteknik >  >> RDS >> Sqlserver

SQL Server villkorligt flöde

Jag skulle skriva om testet som

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

Detta garanterar kortslutning som beskrivs här men betyder att du måste välja den billigaste att utvärdera i förväg istället för att överlåta det till optimeraren.

I mina extremt begränsade tester nedan verkade följande stämma när jag testade

1. EXISTS AND EXISTS

EXISTS AND EXISTS versionen verkar mest problematisk. Detta kedjer ihop några yttre halvfogar. I inget av fallen ändrade den ordningen på testerna för att försöka göra det billigare först (en fråga som diskuterades i andra halvan av detta blogginlägg). I IF ... version skulle det inte ha gjort någon skillnad om det hade gjort det eftersom det inte kortslutit. Men när detta kombinerade predikat sätts i en WHERE klausul planen ändras och den gör kortslutning så att omarrangemang kunde ha varit fördelaktigt.

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

*/

Planerna för alla dessa verkar mycket lika. Anledningen till skillnaden i beteende mellan SELECT 1 WHERE ... version och IF ... versionen är att för den förra om villkoret är falskt så är det korrekta beteendet att inte returnera något resultat så det bara kedjar OUTER SEMI JOINS och om en är falsk så går noll rader vidare till nästa.

Men IF version alltid måste returnera ett resultat på 1 eller noll. Den här planen använder en sondkolumn i dess yttre kopplingar och ställer in detta på falskt om EXISTS testet är inte godkänt (snarare än att bara kassera raden). Det betyder att det alltid finns en rad som matas in i nästa Join och den exekveras alltid.

CASE versionen har en mycket liknande plan men den använder en PASSTHRU predikat som den använder för att hoppa över körningen av JOIN om föregående THEN villkoret var inte uppfyllt. Jag är inte säker på varför man kombinerade AND s skulle inte använda samma tillvägagångssätt.

2. EXISTS OR EXISTS

EXISTS OR EXISTS versionen använde en sammanlänkning (UNION ALL )-operator som den inre ingången till en yttre semi-join. Detta arrangemang innebär att den kan sluta begära rader från insidan så snart den första returneras (dvs. den kan effektivt kortsluta). Alla fyra frågorna slutade med samma plan där det billigare predikatet utvärderades först.

/*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. Lägger till en ELSE

Det föll mig in att pröva De Morgans lag för att konvertera AND till OR och se om det gjorde någon skillnad. Konvertering av den första frågan ger

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

Så detta gör fortfarande ingen skillnad för kortslutningsbeteendet. Men om du tar bort NOT och vänd ordningen på IF ... ELSE villkor det nu gör kortslutning!

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. PostgreSQL Index vs InnoDB Index - Förstå skillnaderna

  2. Var är mitt ogiltiga tecken (ORA-00911)

  3. Klusterverifieringsverktyg som genererar ett stort antal xml-filer på filsystemet "/u01".

  4. SQL Server Destination vs OLE DB Destination