sql >> Database teknologi >  >> RDS >> Sqlserver

Topsvar på 5 brændende spørgsmål om COALESCE-funktion i SQL Server

Hvor cool er COALESCE-funktionen i SQL?

Det er fedt nok til at være så vigtigt for mig. Og jeg vil være mere end glad for at ansætte en ny fyr, der ikke har en dårlig vane med at ignorere målet med COALESCE. Det inkluderer andre udtryk og funktioner til at håndtere lignende situationer.

I dag finder du svarene på de fem mest stillede spørgsmål om SQL COALESCE-udtryk. En af disse bliver debatteret igen og igen.

Skal vi begynde?

Hvad er brugen af ​​COALESCE-funktionen i SQL?

Det kan besvares med 2 ord:håndtering af nuller .

Nul er et tomrum af værdier. Med andre ord ukendt. Det er forskelligt fra en tom streng eller et nultal. Håndtering af nuller kræver brug af udtryk og funktioner. En af dem er COALESCE.

For at forstå, hvad jeg mener, se udsagn nedenfor:

DECLARE @number INT

SELECT @number + 33587 

Vil det køre fint? Det vil.

Er der et problem? Ingen i øjeblikket.

Men udsagn vil resultere i NULL, fordi tilføjelse af null til et tal er lig med NULL.

Hvis alle dine forespørgsler kun falder til dette niveau, kan du stoppe med at læse. Men det er de selvfølgelig ikke. Vi bliver betalt for mere end at producere denne slags kode.

Lad os nu tilføje en lille smule 'katastrofe':

DECLARE @number INT

SET @number = @number + 33587

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

PRINT 'Success!' 

Udførelsen af ​​koden ovenfor vil være i blindgyden, når den når UPDATE-sætningen. Det vil ikke udskrive 'Succes!', fordi du ikke kan sætte et nul på en kolonne, der ikke kan nulstilles. Siger denne udtalelse klart, hvorfor vi skal håndtere nuller?

Lad os ændre koden for at tilføje et sikkerhedsnet:

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 vil ændre nulværdien til nul, og en sum vil ikke være nul.

Læren er, at COALESCE er et af sikkerhedsnettene mod nuller. Endnu bedre, håndtering af nuller korrekt i din SQL-kode reducerer din hovedpine og lader dig gå tidligt hjem. Det er helt sikkert.

Nu forstår du, hvorfor jeg vil have nogen i mit team, der er flittige til at håndtere nuller.

Flere praktiske eksempler på brugen af ​​SQL COALESCE

Lad os henvise til mere praktiske eksempler.

Antag, at du bor i en region, hvor nogle mennesker har mellemnavne, men andre ikke har. Hvordan vil du danne det fulde navn ud af fornavnet, mellemnavnet og efternavnet uden at falde i nulfælden?

Her er en mulig løsning ved at bruge COALESCE:

USE AdventureWorks
GO

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

Et andet eksempel:antag, at du er ansat i en virksomhed, hvor bruttoløn beregnes forskelligt for hver medarbejder. For nogle af dem er der timepriser. Andre får betalt til ugentlige eller månedlige takster.

Her er et tabeleksempel sammen med en forespørgselsløsning ved hjælp af 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; 

Tabellen indeholder forskellige lønformer pr. medarbejder-id. Forespørgslen kræver, at du angiver en månedlig løn for alle.

Det er her COALESCE vil skinne:det accepterer en liste over værdier, og der kan være et hvilket som helst antal elementer. COALESCE vil vælge den første, der ikke er nul:

Pænt, ikke?

Hvordan fungerer COALESCE i SQL?

Definitionen af ​​COALESCE er et udtryk, der returnerer den første ikke-nul værdi fra en liste over værdier. COALESCE-syntaksen er:

COALESCE ( udtryk [ ,…n ] )

Vores tidligere eksempel med forskellige betalingsformer for løn illustrerer dette.

Hvad er der under hætten

Under motorhjelmen er COALESCE-funktionen i SQL et sukkerbelagt udtryk for et meget længere CASE-udtryk. Det eliminerer behovet for at skrive en tilsvarende CASE, som er længere (og trættende, for dovne maskinskrivere som mig). Resultatet bliver det samme.

Kan vi bevise det? Ja! For det første indrømmer Microsoft det.

Men godt for os, Microsoft inkluderede det i udførelsesplanen. Således ved vi, hvad der foregår.

Lad os prøve det ved at bruge et tidligere eksempel med løn. Men før du kører forespørgslen nedenfor igen, skal du aktivere Inkluder faktisk eksekveringsplan eller tryk bare på CTRL-M .

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

Klik på Udførelsesplan fanen i resultaterne. Det ser enkelt ud, men vores skjulte perle ligger i Compute Scalar node. Når du holder musen over det, ser du et udtryk ved navn Expr1002 (Figur 2). Hvad kan det være?

Lad os grave dybere. Højreklik på det, og vælg Vis udførelsesplan XML . Et nyt vindue vises. Tag et kig på figur 3 nedenfor:

Der er din CASE-erklæring. Nedenfor er det hele formateret og indrykket for læselighed:

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 

Det er ret langt i forhold til

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

Det er, hvad SQL Server gjorde med vores forespørgsel med COALESCE. Alt er for at få den første værdi, der ikke er null i en liste over værdier.

Kan det blive kortere?

Jeg ved, hvad du tænker. Hvis SQL Server gør det under forespørgselsbehandling, skal COALESCE være langsom. For ikke at nævne de mange optrædener af CONVERT_IMPLICIT. Vil du hellere bruge alternativer?

For det første kan du selv skrive en kortere CASE-erklæring. Eller du kan bruge ISNULL. Mere om det senere. Apropos at være langsom, så fik jeg det dækket, inden dette indlæg slutter.

Hvad er forskellene mellem COALESCE og ISNULL i SQL?

Et af alternativerne til COALESCE er ISNULL. Brug af COALESCE med 2 værdier gør det ligner ISNULL. Resultaterne ligner i hvert fald. Alligevel er der bemærkelsesværdige forskelle. Du kan bruge det som en guide til at beslutte, om du vil bruge COALESCE eller ISNULL.

(1) ISNULL accepterer 2 argumenter. COALESCE accepterer en liste over argumenter

Det er den mest åbenlyse forskel. Ud fra syntaksen er det helt sikkert anderledes.

ISNULL ( check_expression , replacement_value )
COALESCE ( udtryk [ ,…n ] )

Når du bruger begge med 2 argumenter, er resultaterne de samme. De 2 udsagn nedenfor vil resultere i 1:

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

Selvom resultaterne er de samme, menes de anderledes:

  • ISNULL(NULL, 1) returnerede 1, fordi det første argument er NULL.
  • COALESCE(NULL, 1) returnerede 1, fordi 1 er den første ikke-nul værdi på listen .

(2) COALESCE er SQL-92 Standard

Det er rigtigt. Du kan tjekke det. For eksempel, hvis du vil portere din SQL-kode til MySQL fra SQL Server, vil COALESCE fungere på samme måde. Se figur 4 og sammenlign resultatet fra figur 1:

Brug af ISNULL i MySQL vil dog udløse en fejl, hvis du bruger SQL Servers syntaks.

Kør f.eks. følgende i SQL Server Management Studio og MySQL Workbench:

SELECT ISNULL(null,1) 

Hvad skete der? I SQL Server er outputtet 1. Men i MySQL er outputtet en fejl:

06:36:52 SELECT ISNULL(null,1) Fejlkode:1582. Forkert parameterantal i kaldet til den oprindelige funktion 'ISNULL'

Sagen er, at ISNULL i MySQL accepterer 1 argument og returnerer 1, hvis argumentet er null. Ellers returnerer den 0.

(3) Passering af 2 nuller i COALESCE udløser en fejl. Det er fint med ISNULL

Dette vil udløse en fejl:

SELECT COALESCE(NULL, NULL) 

Fejlen er:'Mindst et af argumenterne til COALESCE skal være et udtryk, der ikke er NULL-konstanten.'

Dette vil være fint:

SELECT ISNULL(NULL, NULL) 

Ændring af COALESCE-koden til noget lignende nedenfor vil ikke udløse en fejl:

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

Det skete fordi @value er ikke en nulkonstant.

(4) SQL COALESCE konverteres til CASE. ISNULL forbliver ISNULL

Vi har set dette i 'Hvordan fungerer COALESCE i SQL?' afsnit. Lad os her se på et andet eksempel:

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

Inspektion af Execution Plan XML for skalaroperatoren afslører konverteringen til 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

Kør nu en tilsvarende forespørgsel ved hjælp af ISNULL:

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

Undersøg derefter Execution Plan XML for skalaroperatoren:

[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 er stadig ISNULL.

(5) Datatypen for det resulterende udtryk er anderledes

Datatypebestemmelse af det resulterende udtryk er også forskellig mellem COALESCE og ISNULL:

  • ISNULL bruger datatypen for den første parameter.
  • COALESCE returnerer datatypen for værdien med den højeste prioritet.

Se dette link for en liste over datatypernes forrang.

Lad os tage et eksempel:

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

Undersøg derefter konverteringen til CASE i 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 

I CASE-udtrykket ovenfor vil datatypen for resultatet være numerisk(19,4).

Hvorfor? Det har højere forrang end penge og småpenge selvom du CAST det til penge . Hvorfor numerisk og ikke penge ? På grund af konstanten 0,0000.

Hvis du undrer dig over, hvad @1 er, kan du bruge Execution Plan XML har svaret. Det er det konstante tal 4.

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

Lad os prøve det med ISNULL:

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

Se igen efter Scalar Operator 's ScalarString :

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

Endelig vil datatypen for det resulterende udtryk være penge . Det er datatypen for det første argument.

Sådan overlistes dataprioritet

Du kan 'overliste' dataprioritet ved at tilføje et par ændringer til din kode. Tidligere havde resultatet et numerisk datatype. Hvis du ønsker, at resultatet skal være en penge datatype og slippe af med CONVERT_IMPLICIT, gør følgende:

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

Har du bemærket konstanterne ($4.000) og ($0.0000)? Det er penge konstanter. Hvad der derefter sker, vises i 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 

Det er meget bedre. Den er kortere, og CONVERT_IMPLICIT er væk. Og den resulterende datatype er penge .

(6) Nullbarheden af ​​det resulterende udtryk er anderledes

ISNULL(NULL, 1) og COALESCE(NULL, 1) har lignende resultater, men deres nullabilitetsværdier er forskellige. COALESCE er nullbar. ISNULL er ikke. Du kan se dette, når du bruger det på beregnede kolonner.

Lad os henvise til et eksempel. Udsagnet nedenfor vil udløse en fejl, fordi PRIMARY KEY ikke kan acceptere NULL-værdier. Samtidig er nullabiliteten af ​​COALESCE-udtrykket for kolonne2 evalueres til NULL.

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

Denne sætning lykkes, fordi nullabiliteten af ​​ISNULL-funktionen evalueres SOM IKKE NULL.

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

(7) ISNULLs venstre argument evalueres én gang. Det er det modsatte med COALESCE

Overvej det forrige eksempel igen:

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

Undersøg derefter ScalarString til dette:

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 

Når COALESCE-funktionen i SQL konverteres til en CASE, evalueres hvert udtryk to gange (undtagen det sidste). Som du kan se ovenfor, hourly_rate*22.00*8.00 dukkede op to gange. Det samme med ugentlig_pris*4,00 . Det sidste udtryk, månedlig_pris , dukkede op én gang.

Da COALESCE vil evaluere udtryk to gange, kan der være en præstationsstraf. Mere om dette senere.

Tjek dog ISNULL-ækvivalenten:

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

Derefter inspicerer vi ScalarString i 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))

Som du kan se ovenfor, hourly_rate , ugentlig_pris og månedlig_pris dukkede kun op én gang. Så du behøver ikke bekymre dig om, at udtryk bliver evalueret to gange med ISNULL.

Kan vi bruge COALESCE i en WHERE-klausul?

Det er sikkert. Der er ingen bedre måde end at vise et eksempel for at bevise dette.

-- 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 returnerer en tom streng hvis Mellemnavn er NULL. Selvfølgelig er der en kortere måde at producere resultatet på. Men dette viser, at COALESCE fungerer i en WHERE-klausul.

Hvad er hurtigere:COALESCE eller ISNULL?

Endelig er vi kommet til et varmt emne:Performance!

Du får mange sider med tests og sammenligninger, men der vil være en kamp mellem COALESCE og ISNULL-tilhængere i kommentarerne. Vi vil fjerne støvet og røgen fra disse krige.

Hvad er hurtigere:COALESCE eller ISNULL? Lad mig begynde med at sige, at svaret er:

(trommer ruller)

DET AFHÆNGER!

(kæben faldt)

Skuffet? Jeg vil forklare det om et øjeblik.

For det første er jeg enig i, at begge ser ud til at have præstationsforskelle, hvis du bruger forløbet tid som din metrik. Nogle mennesker støttede dette faktum, når SQL Server oversætter COALESCE til CASE-sætninger. I mellemtiden forbliver ISNULL som den er.

Andre kan ræsonnere anderledes på grund af de varierende resultater. Også for dem er det hurtigere at konvertere COALESCE til CASE, end vi blinker med øjnene. Ligesom hvad der er påpeget i denne tråd, er præstationsforskellen 'miniscul'. Jeg er enig. Her er en anden artikel, der sagde, at forskellen 'er mindre.'

Men her er en stor sag:kan vi stole på en minimal forløbet tid som en metrik? Det, der virkelig betyder noget, er, hvor meget logisk læsning forespørgslen har. Det er det, vi skal påpege i vores næste eksempler.

(Tjek min anden artikel om logiske læsninger og hvorfor denne faktor, der halter dine forespørgsler.)

Eksempel 1

Lad os undersøge det samme eksempel og sammenligne deres logiske læsninger og udførelsesplaner. Før du kører dette, skal du sørge for, at STATISTICS IO er TIL og Inkluder faktisk eksekveringsplan er aktiveret.

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 

Her er fakta:

Logiske læsninger for begge forespørgsler er de samme. Begge er 107 * 8KB sider. Hvis vi har en million poster, vil logiske aflæsninger naturligvis stige. Men de logiske læsninger for begge forespørgsler vil være ens. Det er tilfældet, selvom COALESCE bliver konverteret til CASE:

Lad os inspicere udførelsesplanen. Sådan skal vi gøre det:

  1. Udfør den 1. SELECT-sætning med COALESCE-udtrykket.
  2. Klik på Udførelsesplan fane i resultater. Højreklik på den, og vælg Gem eksekveringsplan som . Og navngiv filen plan1.sqlplan .
  3. Udfør den 2. SELECT-sætning med ISNULL-funktionen.
  4. Klik på Udførelsesplan fane i resultater.
  5. Højreklik på den, og vælg Sammenlign Showplan .
  6. Vælg filen plan1.sqlplan . Et nyt vindue vises.

Det er sådan, vi skal inspicere udførelsesplanen for alle eksempler.

For at komme tilbage til vores første eksempel, se figur 6 for at se sammenligningen af ​​eksekveringsplanen:

Lagde du mærke til disse vigtige punkter i figur 6?

  • Den skraverede del af de 2 planer (indeksscanningerne) betyder, at SQL Server brugte de samme operationer til de 2 forespørgsler.
  • QueryPlanHash for de 2 planer er 0x27CEB4CCE12DA5E7, hvilket betyder at planen er den samme for begge.
  • De få millisekunders forskelle for forløbet tid er ubetydelige.

Takeaways

Det er som at sammenligne æbler med æbler, men af ​​forskellige typer. Et er et Fuji-æble fra Japan, et andet er et rødt æble fra New York. Alligevel er de begge æbler.

På samme måde kræver SQL Server de samme ressourcer og den valgte eksekveringsplan for begge forespørgsler. Den eneste forskel er brugen af ​​COALESCE eller ISNULL. Mindre forskelle, da det endelige resultat er det samme.

Eksempel 2

Den store forskel viser sig, når du bruger en underforespørgsel som argument til både COALESCE og 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) 

Ovenstående kode vil have det samme resultat, men indersiden er meget anderledes.

Lad os starte med de logiske læsninger:

SELECT-sætningen med COALESCE-udtrykket har den dobbelte logiske læsning af den, der brugte ISNULL.

Men hvorfor fordoble de logiske læsninger? Sammenligningen af ​​eksekveringsplanen vil afsløre endnu mere:

Figur 8 forklarer, hvorfor de logiske aflæsninger er dobbelte ved brug af COALESCE. Se de 2 Stream Aggregate noder i den nederste plan:de er dubletter. Det næste spørgsmål er, hvorfor blev det duplikeret?

Lad os huske det punkt, der vedrører, når COALESCE konverteres til CASE. Hvor mange gange vurderes udtrykkene i argumenterne? TO GANGE!

Så underforespørgslen evalueres to gange. Det vises i udførelsesplanen med duplikerede noder.

Dette forklarer også dobbelt logisk læsning ved brug af COALESCE sammenlignet med ISNULL. Hvis du planlægger at se på udførelsesplanens XML for forespørgslen med COALESCE, er den ret lang. Men det afslører, at underforespørgslen vil blive udført to gange.

Hvad nu? Kan vi overliste dette? Selvfølgelig! Hvis du nogensinde har stået over for noget lignende, som jeg tror er sjældent, er den mulige løsning at dele og erobre. Eller brug ISNULL, hvis det kun er én underforespørgsel.

Sådan undgår du at evaluere underforespørgselsudtrykket to gange

Sådan undgår du at evaluere underforespørgslen to gange:

  • Deklarer en variabel, og tildel resultatet af underforespørgslen til den.
  • Send derefter variablen som et argument til COALESCE.
  • Gentag de samme trin afhængigt af antallet af underforespørgsler.

Det burde som sagt være sjældent, men hvis det sker, ved du, hvad du skal gøre nu.

Et par ord på isolationsniveau

Evaluering af underforespørgslen to gange kan forårsage endnu et problem. Afhængigt af din forespørgsels isolationsniveau kan resultatet af den første evaluering være anderledes end den anden i et flerbrugermiljø. Det er vanvittigt.

For at sikre, at de stabile resultater vender tilbage, kan du prøve at bruge en SNAPSHOT ISOLATION. Du kan også bruge ISNULL. Eller det kan være en del-og-hersk tilgang, som påpeget ovenfor.

Takeaways

Så hvad er essensen?

  • Tjek altid de logiske læsninger. Det betyder mere end forløbet tid. Brug den forløbne tid og fjern din fornuft. Din maskine og produktionsserveren vil altid have varierende resultater. Giv dig selv en pause.
  • Tjek altid udførelsesplanen, så du ved, hvad der sker under emhætten.
  • Vær opmærksom på, at når COALESCE oversættes til CASE, evalueres udtrykkene to gange. Så kan en underforespørgsel eller noget lignende være et problem. At tildele underforespørgselsresultatet til en variabel, før den bruges i COALESCE, kan være en løsning.
  • Hvad der er hurtigere afhænger af de logiske læsningers resultater og udførelsesplaner. Der er ingen generel regel til at afgøre, om COALESCE eller ISNULL er hurtigere. Ellers kan Microsoft informere om det, som de gjorde i HierarchyID og SQL Graph.

Til sidst afhænger det virkelig af.

Konklusion

Jeg håber, du har haft det værd at læse denne artikel. Her er de punkter, vi diskuterede:

  • COALESCE er en af ​​måderne at håndtere nuller på. Det er et sikkerhedsnet for at undgå fejl i kode.
  • Den accepterer argumentlisten og returnerer den første ikke-nul værdi.
  • Det bliver konverteret til et CASE-udtryk under forespørgselsbehandlingen, men det forsinker ikke forespørgslen.
  • Selvom der er nogle få ligheder med ISNULL, er der 7 bemærkelsesværdige forskelle.
  • Det kan bruges med WHERE-sætningen.
  • Endelig er det ikke hurtigere eller langsommere end ISNULL.

Hvis du kan lide dette indlæg, venter knapperne på de sociale medier på dit klik. Deling er omsorg.

Tak.

Læs også

Håndtering af NULL-værdierne effektivt med SQL COALESCE-funktionen for begyndere

En praktisk brug af SQL COALESCE-funktionen


  1. Er der nogen forskel mellem DECIMAL og NUMERIC i SQL Server?

  2. Udførelsesrækkefølge for SQL-forespørgslen

  3. Bruger samme kolonne flere gange i WHERE-sætning

  4. Dvale native forespørgsel - char(3) kolonne