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

Halloween-problemet – del 3

[ Del 1 | Del 2 | Del 3 | Del 4 ]

MERGE sætning (introduceret i SQL Server 2008) giver os mulighed for at udføre en blanding af INSERT , UPDATE og DELETE operationer ved hjælp af en enkelt erklæring. Halloween-beskyttelsesproblemerne for MERGE er for det meste en kombination af kravene til de enkelte operationer, men der er nogle vigtige forskelle og et par interessante optimeringer, der kun gælder for MERGE .

Undgå Halloween-problemet med MERGE

Vi starter med at se igen på demo- og iscenesættelseseksemplet fra del to:

CREATE TABLE dbo.Demo
(
    SomeKey integer NOT NULL,
 
    CONSTRAINT PK_Demo
        PRIMARY KEY (SomeKey)
);
 
CREATE TABLE dbo.Staging
(
    SomeKey integer NOT NULL
);
 
INSERT dbo.Staging
    (SomeKey)
VALUES
    (1234),
    (1234);
 
CREATE NONCLUSTERED INDEX c 
ON dbo.Staging (SomeKey);
 
INSERT dbo.Demo
SELECT s.SomeKey
FROM dbo.Staging AS s
WHERE NOT EXISTS
(
    SELECT 1
    FROM dbo.Demo AS d
    WHERE d.SomeKey = s.SomeKey
);

Som du måske husker, blev dette eksempel brugt til at vise, at en INSERT kræver Halloween-beskyttelse, når indsættelsesmåltabellen også refereres til i SELECT del af forespørgslen (EXISTS klausul i dette tilfælde). Den korrekte adfærd for INSERT sætningen ovenfor er at forsøge at tilføje begge 1234 værdier, og som følge heraf mislykkes med en PRIMARY KEY krænkelse. Uden faseadskillelse er INSERT ville fejlagtigt tilføje én værdi og fuldføre uden en fejl.

INSERT-udførelsesplanen

Ovenstående kode har én forskel fra den, der blev brugt i del to; et ikke-klynget indeks på Iscenesættelsestabellen er blevet tilføjet. INSERT eksekveringsplan stadig kræver dog Halloween-beskyttelse:

FLEDE-udførelsesplanen

Prøv nu den samme logiske indsættelse udtrykt ved hjælp af MERGE syntaks:

MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED BY TARGET THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey);

Hvis du ikke er bekendt med syntaksen, er logikken der at sammenligne rækker i Staging- og Demo-tabellerne på SomeKey-værdien, og hvis der ikke findes en matchende række i måltabellen (Demo), indsætter vi en ny række. Dette har nøjagtig den samme semantik som den forrige INSERT...WHERE NOT EXISTS kode, selvfølgelig. Udførelsesplanen er dog en helt anden:

Læg mærke til manglen på en ivrig bordspole i denne plan. På trods af det producerer forespørgslen stadig den korrekte fejlmeddelelse. Det ser ud til, at SQL Server har fundet en måde at udføre MERGE på planlægge iterativt, mens du respekterer den logiske faseadskillelse, der kræves af SQL-standarden.

Optimeringen af ​​huludfyldning

Under de rigtige omstændigheder kan SQL Server-optimeringsværktøjet genkende, at MERGE sætningen er hulfyldende , hvilket bare er en anden måde at sige, at sætningen kun tilføjer rækker, hvor der er et eksisterende hul i måltabellens nøgle.

For at denne optimering skal anvendes, skal de værdier, der bruges i WHEN NOT MATCHED BY TARGET klausul skal præcis matche ON del af USING klausul. Måltabellen skal også have en unik nøgle (et krav, der opfyldes af PRIMARY KEY i den foreliggende sag). Hvor disse krav er opfyldt, vises MERGE erklæring kræver ikke beskyttelse mod Halloween-problemet.

Selvfølgelig er MERGE sætning er logisk hverken mere eller mindre hulfyldning end den originale INSERT...WHERE NOT EXISTS syntaks. Forskellen er, at optimeringsværktøjet har fuldstændig kontrol over implementeringen af ​​MERGE sætning, hvorimod INSERT syntaks ville kræve, at den ræsonnerede om den bredere semantik af forespørgslen. Et menneske kan nemt se, at INSERT er også huludfyldende, men optimizeren tænker ikke over tingene på samme måde, som vi gør.

For at illustrere den nøjagtige match krav, jeg nævnte, skal du overveje følgende forespørgselssyntaks, som ikke gør drage fordel af huludfyldningsoptimeringen. Resultatet er fuld Halloween-beskyttelse leveret af en ivrig bordspole:

MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey * 1);

Den eneste forskel er multiplikationen med én i VALUES klausul – noget, der ikke ændrer logikken i forespørgslen, men som er nok til at forhindre, at huludfyldningsoptimeringen anvendes.

Udfyldning af hul med indlejrede løkker

I det foregående eksempel valgte optimeringsværktøjet at slutte sig til tabellerne ved hjælp af en Merge join. Huludfyldningsoptimeringen kan også anvendes, hvor der vælges en Nested Loops join, men dette kræver en ekstra unikhedsgaranti på kildetabellen og en indekssøgning på indersiden af ​​samlingen. For at se dette i aktion kan vi rydde de eksisterende iscenesættelsesdata ud, tilføje entydighed til det ikke-klyngede indeks og prøve MERGE igen:

-- Remove existing duplicate rows
TRUNCATE TABLE dbo.Staging;
 
-- Convert index to unique
CREATE UNIQUE NONCLUSTERED INDEX c 
ON dbo.Staging (SomeKey)
WITH (DROP_EXISTING = ON);
 
-- Sample data
INSERT dbo.Staging
    (SomeKey)
VALUES
    (1234),
    (5678);
 
-- Hole-filling merge
MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey);

Den resulterende udførelsesplan bruger igen huludfyldningsoptimeringen for at undgå Halloween-beskyttelse ved at bruge en indlejret løkkesammenføjning og en indvendig sidesøgning ind i måltabellen:

Undgå unødvendige indeksgennemgange

Hvor hulfyldningsoptimeringen gælder, kan motoren også anvende en yderligere optimering. Den kan huske den aktuelle indeksposition, mens den læser måltabellen (behandler én række ad gangen, husk) og genbrug den information, når du udfører indsættelsen, i stedet for at søge ned i b-træet for at finde indsætningsplaceringen. Begrundelsen er, at den aktuelle læseposition med stor sandsynlighed er på samme side, hvor den nye række skal indsættes. Det er meget hurtigt at kontrollere, at rækken faktisk hører hjemme på denne side, da det kun involverer at kontrollere de laveste og højeste nøgler, der i øjeblikket er gemt der.

Kombinationen af ​​at eliminere Eager Table Spool og gemme en indeksnavigation pr. række kan give en betydelig fordel i OLTP-arbejdsbelastninger, forudsat at eksekveringsplanen hentes fra cachen. Kompileringsomkostningerne for MERGE sætninger er noget højere end for INSERT , UPDATE og DELETE , så planlæg genbrug er en vigtig overvejelse. Det er også nyttigt at sikre, at siderne har tilstrækkelig ledig plads til at rumme nye rækker, så man undgår sideopdelinger. Dette opnås typisk gennem normal indeksvedligeholdelse og tildeling af en passende FILLFACTOR .

Jeg nævner OLTP-arbejdsbelastninger, som typisk indeholder et stort antal relativt små ændringer, fordi MERGE optimeringer er muligvis ikke et godt valg, hvor et stort antal rækker behandles pr. sætning. Andre optimeringer såsom minimalt loggede INSERTs kan på nuværende tidspunkt ikke kombineres med hulfyldning. Som altid bør ydeevneegenskaberne benchmarkes for at sikre, at de forventede fordele realiseres.

Huludfyldningsoptimeringen for MERGE Indsæt kan kombineres med opdateringer og sletninger ved hjælp af yderligere MERGE klausuler; hver dataændringsoperation vurderes separat for Halloween-problemet.

Undgåelse af deltagelse

Den endelige optimering, vi vil se på, kan anvendes, hvor MERGE sætningen indeholder opdaterings- og sletningsoperationer samt en huludfyldningsindsats, og måltabellen har et unikt klynget indeks. Følgende eksempel viser en almindelig MERGE mønster, hvor umatchede rækker indsættes, og matchende rækker opdateres eller slettes afhængigt af en yderligere betingelse:

CREATE TABLE #T
(
    col1 integer NOT NULL,
    col2 integer NOT NULL,
 
    CONSTRAINT PK_T
        PRIMARY KEY (col1)
);
 
CREATE TABLE #S
(
    col1 integer NOT NULL,
    col2 integer NOT NULL,
 
    CONSTRAINT PK_S
        PRIMARY KEY (col1)
);
 
INSERT #T
    (col1, col2)
VALUES
    (1, 50),
    (3, 90);
 
INSERT #S
    (col1, col2)
VALUES
    (1, 40),
    (2, 80),
    (3, 90);

MERGE erklæring, der kræves for at foretage alle de nødvendige ændringer, er bemærkelsesværdigt kompakt:

MERGE #T AS t
USING #S AS s ON t.col1 = s.col1
WHEN NOT MATCHED THEN INSERT VALUES (s.col1, s.col2)
WHEN MATCHED AND t.col2 - s.col2 = 0 THEN DELETE
WHEN MATCHED THEN UPDATE SET t.col2 -= s.col2;

Udførelsesplanen er ret overraskende:

Ingen Halloween-beskyttelse, ingen joinforbindelse mellem kilde- og måltabellerne, og det er ikke ofte, du vil se en Clustered Index Insert-operator efterfulgt af en Clustered Index Merge til den samme tabel. Dette er endnu en optimering rettet mod OLTP-arbejdsbelastninger med høj plangenbrug og passende indeksering.

Ideen er at læse en række fra kildetabellen og straks prøve at indsætte den i målet. Hvis der opstår en nøgleovertrædelse, undertrykkes fejlen, indsæt-operatøren udsender den modstridende række, den fandt, og denne række behandles derefter til en opdatering eller sletning ved at bruge fletplanoperatøren som normalt.

Hvis den originale indsættelse lykkes (uden en nøgleovertrædelse), fortsætter behandlingen med den næste række fra kilden (Flet-operatøren behandler kun opdateringer og sletninger). Denne optimering gavner primært MERGE forespørgsler, hvor de fleste kilderækker resulterer i en indsættelse. Igen, omhyggelig benchmarking er påkrævet for at sikre, at ydeevnen er bedre end at bruge separate udsagn.

Oversigt

MERGE statement giver flere unikke optimeringsmuligheder. Under de rigtige omstændigheder kan det undgå behovet for at tilføje eksplicit Halloween-beskyttelse sammenlignet med en tilsvarende INSERT operation, eller måske endda en kombination af INSERT , UPDATE og DELETE udsagn. Yderligere MERGE -specifikke optimeringer kan undgå gennemgangen af ​​indeks b-træet, som normalt er nødvendig for at lokalisere indsættelsespositionen for en ny række, og kan også undgå behovet for at forbinde kilde- og måltabellerne fuldstændigt.

I den sidste del af denne serie vil vi se på, hvordan forespørgselsoptimeringsværktøjet begrunder behovet for Halloween-beskyttelse, og identificere nogle flere tricks, den kan bruge for at undgå behovet for at tilføje Ivrige bordspoler til udførelsesplaner, der ændrer data.

[ Del 1 | Del 2 | Del 3 | Del 4 ]


  1. org.postgresql.util.PSQLEundtagelse:Store objekter må ikke bruges i auto-commit-tilstand

  2. Hvordan får man en alder fra et D.O.B-felt i MySQL?

  3. Sådan forbinder du to tabeller ved hjælp af en kommasepareret liste i sammenføjningsfeltet

  4. Hvordan man fremskynder indlæsning af data fra oracle sql til pandas df