sql >> Database >  >> RDS >> Sqlserver

Topantwoorden op 5 brandende vragen over de COALESCE-functie in SQL Server

Hoe cool is de COALESCE-functie in SQL?

Het is cool genoeg om zo belangrijk voor me te zijn. En ik zal meer dan blij zijn om een ​​nieuwe man aan te nemen die geen slechte gewoonte heeft om het doel van COALESCE te negeren. Dat omvat andere uitdrukkingen en functies voor het omgaan met vergelijkbare situaties.

Vandaag vindt u de antwoorden op de vijf meest gestelde vragen over SQL COALESCE-expressie. Een daarvan wordt keer op keer besproken.

Zullen we beginnen?

Wat is het gebruik van de COALESCE-functie in SQL?

Het kan in 2 woorden worden beantwoord:nulls afhandelen .

Null is een leegte van waarden. Oftewel onbekend. Het is anders dan een lege tekenreeks of een nulnummer. Het afhandelen van nulls vereist het gebruik van expressies en functies. Een daarvan is COALESCE.

Zie de onderstaande uitspraken om te begrijpen wat ik bedoel:

DECLARE @number INT

SELECT @number + 33587

Zal het goed lopen? Het zal.

Is er een probleem? Geen, op dit moment.

Maar de instructies resulteren in NULL omdat het toevoegen van null aan een getal gelijk is aan NULL.

Als al uw vragen alleen op dit niveau vallen, kunt u stoppen met lezen. Maar dat zijn ze natuurlijk niet. We worden voor meer betaald dan het produceren van dit soort code.

Laten we nu een beetje 'ramp' toevoegen:

DECLARE @number INT

SET @number = @number + 33587

UPDATE myTable
set col1 = @number   -- Surprise! col1 is NOT NULLABLE
where ID = 1

PRINT 'Success!'

De uitvoering van de bovenstaande code zal op het einde lopen wanneer het de UPDATE-instructie bereikt. Er wordt geen 'Succes!' afgedrukt omdat u geen null op een niet-nullable kolom kunt plaatsen. Spreekt deze verklaring duidelijk uit waarom we met nulls moeten omgaan?

Laten we de code wijzigen om een ​​vangnet toe te voegen:

DECLARE @number INT

SET @number = COALESCE(@number,0) + 33587     -- our safety net. Thanks to COALESCE.

UPDATE myTable
set col1 = @number               -- Disaster averted!
where ID = 1

PRINT 'Success!'

COALESCE verandert de null-waarde in nul en een som is niet null.

De les is dat COALESCE een van de vangnetten is tegen nulwaarden. Nog beter, het correct omgaan met nulls in uw SQL-code vermindert uw hoofdpijn en laat u vroeg naar huis gaan. Dat is zeker.

Nu begrijp je waarom ik in mijn team iemand wil die ijverig is in het afhandelen van nulls.

Meer praktische voorbeelden bij het gebruik van SQL COALESCE

Laten we naar meer praktische voorbeelden verwijzen.

Stel dat u in een regio woont waar sommige mensen een tweede voornaam hebben, maar andere niet. Hoe maak je de volledige naam uit de voornaam, middelste naam en achternaam zonder in de nulval te trappen?

Hier is een mogelijke oplossing met COALESCE:

USE AdventureWorks
GO

SELECT
p.LastName + ', ' + p.FirstName + ' ' + COALESCE(p.MiddleName + ' ','') AS FullName
FROM Person.Person p

Nog een voorbeeld:stel dat u een werknemer bent in een bedrijf waar het brutoloon voor elke werknemer anders wordt berekend. Voor een aantal van hen gelden uurtarieven. Anderen worden betaald tegen wekelijkse of maandelijkse tarieven.

Hier is een tabelvoorbeeld samen met een query-oplossing met COALESCE:

-- STEP 1: Create the table
CREATE TABLE EmployeeWages (
    employee_id INT PRIMARY KEY,
    hourly_rate SMALLMONEY,
    weekly_rate SMALLMONEY,
    monthly_rate MONEY,
    CHECK(
        hourly_rate IS NOT NULL OR
        weekly_rate IS NOT NULL OR
        monthly_rate IS NOT NULL)
);

-- STEP 2: Insert data
INSERT INTO
    EmployeeWages(
        employee_id,
        hourly_rate,
        weekly_rate,
        monthly_rate
    )
VALUES
    (1,60, NULL,NULL),
    (2,40, NULL,NULL),
    (3,NULL, 1000,NULL),
    (4,NULL, NULL,7000),
    (5,NULL, NULL,5000);

-- STEP 3: Query the monthly salary.
SELECT
    employee_id,
    COALESCE(
        hourly_rate*22.00*8.00,
        weekly_rate*4.00,
        monthly_rate
    ) AS monthly_salary
FROM
    EmployeeWages;

De tabel bevat verschillende betaalwijzen per werknemer-ID. De zoekopdracht vereist dat u een maandsalaris voor iedereen opgeeft.

Dit is waar COALESCE uitblinkt:het accepteert een lijst met waarden en er kan een willekeurig aantal items zijn. COALESCE kiest de eerste die niet nul is:

Netjes, toch?

Hoe werkt COALESCE in SQL?

De definitie van COALESCE is een expressie die de eerste niet-null-waarde uit een zoeklijst retourneert. De COALESCE-syntaxis is:

COALESCE ( uitdrukking [ ,…n ] )

Ons vorige voorbeeld met verschillende betalingswijzen voor lonen illustreert dit.

Wat zit er onder de motorkap

Onder de motorkap is de COALESCE-functie in SQL een gesuikerde expressie voor een veel langere CASE-expressie. Het elimineert de noodzaak om een ​​gelijkwaardige CASE te typen, die langer is (en vermoeiend voor luie typisten zoals ik). Het resultaat zal hetzelfde zijn.

Kunnen we het bewijzen? Ja! Ten eerste geeft Microsoft het toe.

Maar goed voor ons, Microsoft nam het op in het Uitvoeringsplan. Zo weten we wat er aan de hand is.

Laten we het proberen aan de hand van een eerder voorbeeld met lonen. Maar voordat u de onderstaande query opnieuw uitvoert, schakelt u Include Actual Execution Plan in of druk gewoon op CTRL-M .

SELECT
    employee_id,
    COALESCE(
        hourly_rate * 22.00 * 8.00,
        weekly_rate * 4.00,
        monthly_rate
    ) AS monthly_salary
FROM
    EmployeeWages;

Klik op het Uitvoeringsplan tabblad in de resultaten. Het ziet er eenvoudig uit, maar ons verborgen juweeltje ligt in de Compute Scalar knooppunt. Als je er met de muis overheen beweegt, zie je een uitdrukking met de naam Expr1002 (Figuur 2). Wat zou het kunnen zijn?

Laten we dieper graven. Klik er met de rechtermuisknop op en selecteer Toon XML uitvoeringsplan . Er verschijnt een nieuw venster. Bekijk afbeelding 3 hieronder:

Daar is je CASE-verklaring. Hieronder is het hele ding geformatteerd en ingesprongen voor de leesbaarheid:

CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                            *(22.00)*(8.00) IS NOT NULL
     THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                       [TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)*(22.00)*(8.00),0)
     ELSE CASE WHEN    
          CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)
                                            *(4.00) IS NOT NULL
          THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),                                                         
                       [TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),0)
          ELSE CONVERT_IMPLICIT(numeric(23,8),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)
          END
END

Dat is vrij lang vergeleken met

COALESCE(
        hourly_rate * 22.00 * 8.00,
        weekly_rate * 4.00,
        monthly_rate
        )

Dat is wat SQL Server deed met onze query met COALESCE. Alles is bedoeld om de eerste waarde te krijgen die niet null is in een lijst met waarden.

Kan het korter?

Ik weet wat je denkt. Als SQL Server dat doet tijdens het verwerken van query's, moet COALESCE traag zijn. Om nog maar te zwijgen over de vele verschijningen van CONVERT_IMPLICIT. Gebruik je liever alternatieven?

Ten eerste kunt u zelf een kortere CASE-instructie typen. Of u kunt ISNULL gebruiken. Daarover later meer. Over traag worden gesproken, ik had dat onder de knie voordat dit bericht eindigde.

Wat zijn de verschillen tussen COALESCE en ISNULL in SQL?

Een van de alternatieven voor COALESCE is ISNULL. Het gebruik van COALESCE met 2 waarden maakt het vergelijkbaar met ISNULL. De resultaten lijken in ieder geval op elkaar. Toch zijn er opvallende verschillen. U kunt het als richtlijn gebruiken om te beslissen of u COALESCE of ISNULL gaat gebruiken.

(1) ISNULL Accepteert 2 argumenten. COALESCE accepteert een lijst met argumenten

Het is het meest voor de hand liggende verschil. Vanuit de syntaxis is het zeker anders.

ISNULL ( check_expression , vervangingswaarde )
COALESCE ( uitdrukking [ ,…n ] )

Wanneer u beide gebruikt met 2 argumenten, zijn de resultaten hetzelfde. De 2 onderstaande uitspraken zullen resulteren in 1:

SELECT ISNULL(NULL, 1)
SELECT COALESCE(NULL, 1)

Hoewel de resultaten hetzelfde zijn, zijn ze anders bedoeld:

  • ISNULL(NULL, 1) heeft 1 geretourneerd omdat het eerste argument NULL is.
  • COALESCE(NULL, 1) heeft 1 geretourneerd omdat 1 de eerste niet-null-waarde in de lijst is .

(2) COALESCE is SQL-92-standaard

Dat klopt. Je kan het controleren. Als u bijvoorbeeld uw SQL-code vanuit SQL Server naar MySQL wilt porten, werkt COALESCE hetzelfde. Zie figuur 4 en vergelijk het resultaat van figuur 1:

Het gebruik van ISNULL in MySQL zal echter een fout veroorzaken als u de syntaxis van SQL Server gebruikt.

voer bijvoorbeeld het volgende uit in SQL Server Management Studio en MySQL Workbench:

SELECT ISNULL(null,1)

Wat er is gebeurd? In SQL Server is de uitvoer 1. Maar in MySQL is de uitvoer een fout:

06:36:52 SELECT ISNULL(null,1) Foutcode:1582. Onjuist aantal parameters in de aanroep van native functie 'ISNULL'

Het punt is dat ISNULL in MySQL 1 argument accepteert en 1 retourneert als het argument null is. Anders geeft het 0 terug.

(3) Het doorgeven van 2 nulls in COALESCE leidt tot een fout. Het is prima met ISNULL

Dit zal een fout veroorzaken:

SELECT COALESCE(NULL, NULL)

De fout is:'Ten minste één van de argumenten voor COALESCE moet een uitdrukking zijn die niet de NULL-constante is.'

Dit komt wel goed:

SELECT ISNULL(NULL, NULL)

Als u de COALESCE-code wijzigt in iets soortgelijks hieronder, wordt er geen fout gegenereerd:

DECLARE @value INT = NULL
SELECT COALESCE(@value,null)

Dat gebeurde omdat @value is geen nulconstante.

(4) SQL COALESCE wordt geconverteerd naar CASE. ISNULL blijft ISNULL

We hebben dit gezien in de 'Hoe werkt COALESCE in SQL?' sectie. Laten we hier een ander voorbeeld bekijken:

SELECT
P.LastName + ', ' + P.FirstName + ' ' + COALESCE(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Inspectie van de Execution Plan XML voor de scalaire operator onthult de conversie naar CASE:

 [AdventureWorks].[Person].[Person].[LastName] as [p].[LastName]+N', '
+[AdventureWorks].[Person].[Person].[FirstName] as [p].[FirstName]+N' '
+CASE WHEN ([AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' ') IS NOT NULL
      THEN [AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' '
      ELSE N''
 END

Voer nu een equivalente query uit met ISNULL:

SELECT
P.LastName + ', ' + P.FirstName + ' ' + ISNULL(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Inspecteer vervolgens de Execution Plan XML voor de scalaire operator:

 [AdventureWorks].[Person].[Person].[LastName] as [p].[LastName]+N', '
+[AdventureWorks].[Person].[Person].[FirstName] as [p].[FirstName]+N' '
+isnull([AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' ',N'')

ISNULL is nog steeds ISNULL.

(5) Het gegevenstype van de resulterende expressie is anders

Het bepalen van het gegevenstype van de resulterende uitdrukking is ook verschillend tussen COALESCE en ISNULL:

  • ISNULL gebruikt het datatype van de eerste parameter.
  • COALESCE retourneert het gegevenstype van de waarde met de hoogste prioriteit.

Bekijk deze link voor een lijst met voorrang van gegevenstypes.

Laten we een voorbeeld geven:

SELECT
 employee_id
,COALESCE(CAST(weekly_rate * 4 AS MONEY),0.0000) AS monthly_rate
FROM EmployeeWages

Inspecteer vervolgens de conversie naar CASE in de Execution Plan XML :

CASE WHEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]
                                           *CONVERT_IMPLICIT(smallmoney,[@1],0),0) IS NOT NULL
     THEN CONVERT_IMPLICIT(numeric(19,4), CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]
                                           *CONVERT_IMPLICIT(smallmoney,[@1],0),0),0)
     ELSE (0.0000)
END

In de bovenstaande CASE-expressie is het gegevenstype van het resultaat numeric(19,4).

Waarom? Het heeft een hogere prioriteit dan geld en kleingeld zelfs als je het naar geld . CAST . Waarom numeriek en niet geld ? Vanwege de constante 0,0000.

Voor het geval je je afvraagt ​​wat @1 is, de Execution Plan XML heeft het antwoord. Het is het constante getal 4.

<ParameterList>
 <ColumnReference Column="@1" ParameterDataType="int" ParameterCompiledValue="(4)"  
       ParameterRuntimeValue="(4)" />
</ParameterList>

Laten we het proberen met ISNULL:

SELECT
 employee_id
,ISNULL(CAST(weekly_rate * 4 AS MONEY),0.0000) AS monthly_rate
FROM EmployeeWages

Zoek nogmaals naar de Scalaire operator 's ScalarString :

ISNULL(CONVERT(MONEY,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]*($4.0000),0),($0.0000))

Ten slotte is het gegevenstype van de resulterende uitdrukking geld . Het is het gegevenstype van het eerste argument.

Hoe u de prioriteit van gegevens te slim af kunt zijn

U kunt de voorrang van gegevens te slim af zijn door een paar wijzigingen aan uw code toe te voegen. Eerder had het resultaat een numerieke data type. Als je wilt dat het resultaat een geld is gegevenstype en verwijder de CONVERT_IMPLICIT, doe het volgende:

SELECT
 employee_id
,COALESCE(CAST(weekly_rate AS MONEY) * ($4.0000),($0.0000)) AS monthly_rate
FROM EmployeeWages

Heb je de constanten ($ 4.000) en ($ 0,0000) opgemerkt? Dat zijn geld constanten. Wat er daarna gebeurt, wordt weergegeven in de Execution Plan XML 's ScalarString :

CASE WHEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*($4.0000) IS NOT NULL 
     THEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*($4.0000) 
     ELSE ($0.0000) 
END

Dat is veel beter. Het is korter en de CONVERT_IMPLICIT is verdwenen. En het resulterende gegevenstype is geld .

(6) De NULL-mogelijkheid van de resulterende expressie is anders

ISNULL(NULL, 1) en COALESCE(NULL, 1) hebben vergelijkbare resultaten, maar hun nullabiliteitswaarden zijn anders. COALESCE is nullable. ISNULL is dat niet. U kunt dit zien wanneer u het op berekende kolommen gebruikt.

Laten we naar een voorbeeld verwijzen. De onderstaande instructie veroorzaakt een fout omdat de PRIMARY KEY geen NULL-waarden kan accepteren. Tegelijkertijd is de nullabiliteit van de COALESCE-expressie voor kolom2 evalueert naar NULL.

CREATE TABLE NullabilityDemo  
(  
  column1 INTEGER NULL,  
  column2 AS COALESCE(column1, 0) PRIMARY KEY,  
  column3 AS ISNULL(column1, 0)  
);

Deze instructie slaagt omdat de nullabiliteit van de ISNULL-functie AS NOT NULL evalueert.

CREATE TABLE NullabilityDemo  
(  
  column1 INTEGER NULL,  
  column2 AS COALESCE(column1, 0),  
  column3 AS ISNULL(column1, 0) PRIMARY KEY  
);

(7) Het linkerargument van ISNULL wordt eenmaal geëvalueerd. Het is het tegenovergestelde met COALESCE

Beschouw het vorige voorbeeld nog eens:

SELECT
    employee_id,
    COALESCE(
        hourly_rate*22.00*8.00,
        weekly_rate*4.00,
        monthly_rate
    ) AS monthly_salary
FROM
    EmployeeWages;

Inspecteer vervolgens de ScalarString hiervoor:

CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                              *(22.00)*(8.00) IS NOT NULL 
     THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                                              [TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                              *(22.00)*(8.00),0) 
     ELSE CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)
                                              *(4.00) IS NOT NULL 
               THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                                        [TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),0) 
               ELSE CONVERT_IMPLICIT(numeric(23,8),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0) 
          END 
END

Wanneer de functie COALESCE in SQL wordt geconverteerd naar een CASE, wordt elke expressie twee keer geëvalueerd (behalve de laatste). Zoals je hierboven kunt zien, hourly_rate*22.00*8.00 twee keer verschenen. Hetzelfde met weekly_rate*4.00 . De laatste uitdrukking, monthly_rate , verscheen eenmaal.

Aangezien COALESCE uitdrukkingen twee keer evalueert, kan er een prestatiestraf zijn. Hierover later meer.

Bekijk echter het ISNULL-equivalent:

SELECT
 employee_id,
 ISNULL(hourly_rate * 22.00 * 8.00,ISNULL(weekly_rate * 4.00,monthly_rate)) AS  
                                                       monthly_salary
FROM EmployeeWages

Vervolgens inspecteren we de ScalarString in de Execution Plan XML :

isnull(CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)*(22.00)*(8.00),
       CONVERT_IMPLICIT(numeric(19,8),
isnull(CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),
CONVERT_IMPLICIT(numeric(14,6),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)),0))

Zoals je hierboven kunt zien, uurtarief , weektarief , en maandelijks_tarief slechts één keer verschenen. U hoeft zich dus geen zorgen te maken dat expressies twee keer worden geëvalueerd met ISNULL.

Kunnen we COALESCE gebruiken in een WHERE-clausule?

Natuurlijk. Er is geen betere manier dan een voorbeeld te laten zien om dit te bewijzen.

-- Query all the names in Person table with no middle name

USE AdventureWorks
GO

SELECT
 p.LastName
,p.FirstName
FROM person.Person p
WHERE COALESCE(p.MiddleName,'') = ''

COALESCE retourneert een lege tekenreeks als Middennaam is niets. Natuurlijk is er een kortere manier om het resultaat te produceren. Maar dit laat zien dat COALESCE werkt in een WHERE-clausule.

Wat is sneller:COALESCE of ISNULL?

Eindelijk zijn we bij een hot topic gekomen:Prestaties!

Je krijgt veel pagina's met tests en vergelijkingen, maar er zal een strijd zijn tussen COALESCE- en ISNULL-voorstanders in commentaren. We zullen het stof en de rook van die oorlogen opruimen.

Wat is sneller:COALESCE of ISNULL? Laat ik beginnen met te zeggen dat het antwoord is:

(tromgeroffel)

HET HANGT AF!

(kaak open)

Teleurgesteld? Ik zal het zo meteen uitleggen.

Ten eerste ben ik het ermee eens dat beide prestatieverschillen lijken te hebben als u de verstreken tijd als uw statistiek gebruikt. Sommige mensen ondersteunden dit feit toen SQL Server COALESCE vertaalt naar CASE-instructies. Ondertussen blijft ISNULL zoals het is.

Anderen redeneren misschien anders vanwege de wisselende resultaten. Ook voor hen is het converteren van COALESCE naar CASE sneller dan we met onze ogen knipperen. Net zoals wat in deze thread is opgemerkt, is het prestatieverschil 'miniscuul'. Ik ben het ermee eens. Hier is nog een artikel waarin staat dat het verschil 'klein' is.

Hier is echter een groot probleem:kunnen we een minuscule verstreken tijd als maatstaf vertrouwen? Wat er echt toe doet, is hoeveel logische reads de query heeft. Daar gaan we in onze volgende voorbeelden op wijzen.

(Bekijk mijn andere artikel over logisch lezen en waarom deze factor achterblijft bij uw zoekopdrachten.)

Voorbeeld 1

Laten we hetzelfde voorbeeld bekijken en hun logische lezingen en uitvoeringsplannen vergelijken. Zorg ervoor dat STATISTICS IO is ingeschakeld voordat u dit uitvoert en Include Actual Execution Plan is ingeschakeld.

SET STATISTICS IO ON

SELECT
P.LastName + ', ' + P.FirstName + ' ' + COALESCE(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

SELECT
P.LastName + ', ' + P.FirstName + ' ' + ISNULL(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Dit zijn de feiten:

Logische leesbewerkingen voor beide query's zijn hetzelfde. Beide zijn 107 * 8KB aan pagina's. Als we een miljoen records hebben, zullen de logische waarden natuurlijk toenemen. Maar de logische waarden voor beide query's zijn gelijk. Dat is zelfs het geval als COALESCE wordt geconverteerd naar CASE:

Laten we het uitvoeringsplan inspecteren. Hier is hoe we het gaan doen:

  1. Voer de eerste SELECT-instructie uit met de COALESCE-expressie.
  2. Klik op het Uitvoeringsplan tabblad in resultaten. Klik er met de rechtermuisknop op en selecteer Uitvoeringsplan opslaan als . En noem het bestand plan1.sqlplan .
  3. Voer de 2e SELECT-instructie uit met de ISNULL-functie.
  4. Klik op het Uitvoeringsplan tabblad in resultaten.
  5. Klik er met de rechtermuisknop op en selecteer Showplan vergelijken .
  6. Selecteer het bestand plan1.sqlplan . Er verschijnt een nieuw venster.

Zo gaan we het uitvoeringsplan voor alle voorbeelden inspecteren.

Terugkomend op ons eerste voorbeeld, zie figuur 6 om de vergelijking van het uitvoeringsplan te zien:

Heb je deze belangrijke punten in figuur 6 opgemerkt?

  • Het gearceerde gedeelte van de twee plannen (de indexscans) betekent dat SQL Server dezelfde bewerkingen heeft gebruikt voor de twee zoekopdrachten.
  • De QueryPlanHash voor de 2 abonnementen is 0x27CEB4CCE12DA5E7, wat betekent dat het abonnement voor beide hetzelfde is.
  • De verschillen van enkele milliseconden voor de verstreken tijd zijn te verwaarlozen.

Afhaalmaaltijden

Het is appels met appels vergelijken, maar dan van verschillende soorten. Een daarvan is een Fuji-appel uit Japan, een andere is een rode appel uit New York. Toch zijn het beide appels.

Evenzo vereist SQL Server dezelfde bronnen en het gekozen uitvoeringsplan voor beide query's. Het enige verschil is het gebruik van COALESCE of ISNULL. Kleine verschillen, want het eindresultaat is hetzelfde.

Voorbeeld 2

Het grote verschil wordt zichtbaar wanneer u een subquery gebruikt als argument voor zowel COALESCE als ISNULL:

USE AdventureWorks
GO

SELECT COALESCE(
       (SELECT
        SUM(th.ActualCost)
        FROM Production.TransactionHistory th
        WHERE th.ProductID = 967)
       ,0) 

SELECT ISNULL(
       (SELECT
        SUM(th.ActualCost)
        FROM Production.TransactionHistory th
        WHERE th.ProductID = 967)
       ,0)

De bovenstaande code heeft hetzelfde resultaat, maar de binnenkant is heel anders.

Laten we beginnen met de logische leest:

De SELECT-instructie met de COALESCE-expressie heeft het dubbele van de logische waarden van degene die ISNULL gebruikte.

Maar waarom de logische waarden verdubbelen? De vergelijking van het uitvoeringsplan onthult nog meer:

Figuur 8 legt uit waarom de logische uitlezingen dubbel zijn met COALESCE. Zie de 2 Stream Aggregate-knooppunten in het onderste plan:het zijn duplicaten. De volgende vraag is, waarom is het gedupliceerd?

Laten we ons herinneren dat het punt betrekking heeft op wanneer COALESCE wordt geconverteerd naar CASE. Hoe vaak worden de uitdrukkingen geëvalueerd in de argumenten? TWEE KEER!

De subquery wordt dus twee keer geëvalueerd. Het wordt weergegeven in het uitvoeringsplan met dubbele knooppunten.

Dit verklaart ook dubbel logische uitlezingen met COALESCE in vergelijking met ISNULL. Als u van plan bent om met COALESCE naar de XML van het uitvoeringsplan van de query te kijken, is deze vrij lang. Maar het onthult dat de subquery twee keer zal worden uitgevoerd.

Wat nu? Kunnen we dit te slim af zijn? Natuurlijk! Als je ooit zoiets hebt meegemaakt, wat volgens mij zeldzaam is, is de mogelijke oplossing om te verdelen en te heersen. Of gebruik ISNULL als het maar één subquery is.

Hoe u kunt voorkomen dat u de subquery-expressie twee keer evalueert

Zo voorkom je dat de subquery twee keer moet worden geëvalueerd:

  • Declareer een variabele en wijs het resultaat van de subquery eraan toe.
  • Geef vervolgens de variabele als argument door aan COALESCE.
  • Herhaal dezelfde stappen afhankelijk van het aantal subquery's.

Zoals ik al zei, het zou zeldzaam moeten zijn, maar als het gebeurt, weet je wat je nu moet doen.

Een paar woorden op isolatieniveau

Het twee keer evalueren van de subquery kan een ander probleem veroorzaken. Afhankelijk van het isolatieniveau van uw query kan het resultaat van de eerste evaluatie verschillen van de tweede in een omgeving met meerdere gebruikers. Het is te gek.

Om ervoor te zorgen dat de stabiele resultaten terugkeren, kunt u proberen een MOMENTOPNAME-ISOLATIE te gebruiken. U kunt ook ISNULL gebruiken. Of het kan een verdeel-en-heersbenadering zijn, zoals hierboven aangegeven.

Afhaalmaaltijden

Dus, wat is de essentie?

  • Controleer altijd de logische waarden. Het is belangrijker dan de verstreken tijd. Gebruik de verstreken tijd en neem uw gezond verstand weg. Uw machine en de productieserver zullen altijd wisselende resultaten hebben. Gun jezelf een pauze.
  • Controleer altijd het uitvoeringsplan, zodat je weet wat er onder de motorkap gebeurt.
  • Houd er rekening mee dat wanneer COALESCE wordt vertaald naar CASE, de uitdrukkingen twee keer worden geëvalueerd. Dan kan een subquery of iets dergelijks een probleem zijn. Het toewijzen van het subqueryresultaat aan een variabele voordat het in COALESCE wordt gebruikt, kan een oplossing zijn.
  • Wat sneller is, hangt af van de resultaten en uitvoeringsplannen van de logische uitlezingen. Er is geen algemene regel om te bepalen of COALESCE of ISNULL sneller is. Anders zou Microsoft daarover kunnen informeren, zoals ze deden in HierarchyID en SQL Graph.

Uiteindelijk hangt het er echt van af.

Conclusie

Ik hoop dat je het de moeite waard vond om dit artikel te lezen. Dit zijn de punten die we hebben besproken:

  • COALESCE is een van de manieren om met nulls om te gaan. Het is een vangnet om fouten in de code te voorkomen.
  • Het accepteert de lijst met argumenten en retourneert de eerste niet-null-waarde.
  • Het wordt geconverteerd naar een CASE-expressie tijdens het verwerken van de query, maar het vertraagt ​​de query niet.
  • Hoewel er enkele overeenkomsten zijn met ISNULL, zijn er 7 opmerkelijke verschillen.
  • Het kan worden gebruikt met de WHERE-component.
  • Ten slotte is het niet sneller of langzamer dan ISNULL.

Als je dit bericht leuk vindt, wachten de social media-knoppen op je klik. Delen is zorgzaam.

Dank je.

Lees ook

Effectief omgaan met de NULL-waarden met de SQL COALESCE-functie voor beginners

Een praktisch gebruik van de SQL COALESCE-functie


  1. Tabel is 'alleen lezen'

  2. ORDER BY-items moeten in de selectielijst verschijnen als SELECT DISTINCT is opgegeven

  3. Oracle Instantclient installeren op Mac OS/X zonder omgevingsvariabelen in te stellen?

  4. MySQL:sorteer GROUP_CONCAT-waarden