sql >> Database >  >> RDS >> Sqlserver

SQL Server voorwaardelijke stroom

Ik zou de test herschrijven als

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

Dit garandeert kortsluiting zoals hier beschreven, maar het betekent wel dat u de goedkoopste moet selecteren om vooraf te evalueren in plaats van dit aan de optimiser over te laten.

In mijn extreem beperkte tests hieronder leek het volgende waar te zijn tijdens het testen

1. EXISTS AND EXISTS

De EXISTS AND EXISTS versie lijkt het meest problematisch. Dit koppelt enkele buitenste semi-joins aan elkaar. In geen van de gevallen heeft het de volgorde van de tests aangepast om te proberen de goedkopere eerst te doen (een probleem dat in de tweede helft van deze blogpost wordt besproken). In de IF ... versie zou het geen verschil hebben gemaakt als het had gedaan omdat het geen kortsluiting maakte. Wanneer dit gecombineerde predikaat echter in een WHERE . wordt geplaatst clausule verandert het plan en het doet kortsluiting, zodat herschikking gunstig had kunnen zijn.

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

*/

De plannen voor al deze lijken erg op elkaar. De reden voor het verschil in gedrag tussen de SELECT 1 WHERE ... versie en de IF ... versie is dat voor de eerste, als de voorwaarde onwaar is, het juiste gedrag is om geen resultaat te retourneren, dus het koppelt gewoon de OUTER SEMI JOINS en als er een onwaar is, worden nul rijen overgedragen naar de volgende.

Maar de IF versie altijd moet een resultaat van 1 of nul retourneren. Dit plan gebruikt een probe-kolom in de buitenste joins en stelt deze in op false als de EXISTS test niet is geslaagd (in plaats van simpelweg de rij weg te gooien). Dit betekent dat er altijd 1 rij wordt ingevoerd in de volgende Join en deze wordt altijd uitgevoerd.

De CASE versie heeft een zeer vergelijkbaar plan, maar het gebruikt een PASSTHRU predikaat dat het gebruikt om de uitvoering van de JOIN over te slaan als de vorige THEN voorwaarde niet werd voldaan. Ik weet niet zeker waarom ik AND . heb gecombineerd s zou niet dezelfde aanpak gebruiken.

2. EXISTS OR EXISTS

De EXISTS OR EXISTS versie gebruikte een aaneenschakeling (UNION ALL ) operator als de binnenste invoer voor een buitenste semi-join. Deze regeling betekent dat het kan stoppen met het aanvragen van rijen vanaf de binnenkant zodra de eerste is geretourneerd (d.w.z. het kan effectief kortsluiting veroorzaken). Alle 4 de zoekopdrachten eindigden met hetzelfde plan waarbij het goedkopere predikaat het eerst werd geëvalueerd.

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

Het kwam bij me op om de wet van De Morgan te proberen om AND . te converteren naar OR en kijk of dat iets uitmaakt. Het converteren van de eerste zoekopdracht geeft

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

Voor het kortsluitgedrag maakt dit dus nog steeds niets uit. Als u echter de NOT en keer de volgorde van de IF ... ELSE . om voorwaarden die het nu doet kortsluiting!

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. cx_Oracle en Exception Handling - Goede praktijken?

  2. PostgreSQL-beveiliging standaardiseren in multi-cloudomgevingen

  3. Bereken werkuren tussen 2 datums in PostgreSQL

  4. MySQL-installatie:FOUT:kan de native extensie voor gem niet bouwen