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

Hvorfor flere JOINs er dårlige for forespørgsel eller ikke kommer i vejen for Optimizer

For nylig stødte jeg på et program, der genererede DB-forespørgsler. Jeg forstår, at der ikke er noget nyt i det, men da applikationen begyndte at køre langsomt, og jeg skulle finde ud af årsagen til afmatningen, blev jeg overrasket over at finde disse forespørgsler. Her er hvad SQL Server nogle gange skal håndtere:

SELECT COUNT(DISTINCT "pr"."id") FROM  ((((((((((((((((("SomeTable" "pr"
LEFT OUTER JOIN "SomeTable1698" "uf_pr_id_698" ON "uf_pr_id_698"."request" = "pr"."id") 
LEFT OUTER JOIN "SomeTable1700" "ufref3737_i2" ON "ufref3737_i2"."request" = "pr"."id") 
LEFT OUTER JOIN "SomeTable1666" "x0" ON "x0"."request" = "ufref3737_i2"."f6_callerper")
LEFT OUTER JOIN "SomeTable1666" "uf_ufref4646_i3_f58__666" ON "uf_ufref4646_i3_f58__666"."request" = "ufref3737_i2"."f58_")
LEFT OUTER JOIN "SomeTable1694" "x1" ON "x1"."request" = "ufref3737_i2"."f38_servicep")
LEFT OUTER JOIN "SomeTable3754" "ufref3754_i12" ON "pr"."id" = "ufref3754_i12"."request")
LEFT OUTER JOIN "SomeTable1698" "uf_ufref3754_i12_reference_698" ON "uf_ufref3754_i12_reference_698"."request" = "ufref3754_i12"."reference")
LEFT OUTER JOIN "SomeTable1698" "x2" ON "x2"."request" = "ufref3737_i2"."f34_parentse")
LEFT OUTER JOIN "SomeTable4128" "ufref3779_4128_i14" ON "ufref3737_i2"."f34_parentse" = "ufref3779_4128_i14"."request")
LEFT OUTER JOIN "SomeTable1859" "x3" ON "x3"."request" = "ufref3779_4128_i14"."reference")
LEFT OUTER JOIN "SomeTable3758" "ufref3758_i15" ON "pr"."id" = "ufref3758_i15"."request")
LEFT OUTER JOIN "SomeTable1698" "uf_ufref3758_i15_reference_698" ON "uf_ufref3758_i15_reference_698"."request" = "ufref3758_i15"."reference")
LEFT OUTER JOIN "SomeTable3758" "ufref3758_i16" ON "pr"."id" = "ufref3758_i16"."request")
LEFT OUTER JOIN "SomeTable4128" "ufref3758_4128_i16" ON "ufref3758_i16"."reference" = "ufref3758_4128_i16"."request")
LEFT OUTER JOIN "SomeTable1859" "x4" ON "x4"."request" = "ufref3758_4128_i16"."reference")
LEFT OUTER JOIN "SomeTable4128" "ufref4128_i17" ON "pr"."id" = "ufref4128_i17"."request")
LEFT OUTER JOIN "SomeTable1859" "uf_ufref4128_i17_reference_859" ON "uf_ufref4128_i17_reference_859"."request" = "ufref4128_i17"."reference")
LEFT OUTER JOIN "SomeTable1666" "uf_ufref4667_i25_f69__666" ON "uf_ufref4667_i25_f69__666"."request" = "uf_pr_id_698"."f69_"
WHERE ("uf_pr_id_698"."f1_applicant" IN (248,169,180,201,203,205,209,215,223,357,371,379,3502,3503,3506,3514,3517,3531,3740,3741)
OR "x0"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "uf_ufref4646_i3_f58__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "uf_ufref4667_i25_f69__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 111)  AND "ufref3737_i2"."f96_" = 0   AND (("ufref3737_i2"."f17_source"  Is Null OR "ufref3737_i2"."f17_source"  <> 566425)
AND ("ufref3737_i2"."f17_source"  Is Null OR "ufref3737_i2"."f17_source"  <> 566424)  OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 56) )
AND ("uf_pr_id_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "x1"."f19_restrict" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "uf_ufref3754_i12_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "x2"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "x3"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) 
OR "uf_ufref3758_i15_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) 
OR "x4"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "uf_ufref4128_i17_reference_859"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136))
AND ("uf_pr_id_698"."f12_responsi"  Is Null OR "uf_pr_id_698"."f12_responsi"  <> 579420)  ) AND "pr"."area" IN (700) AND "pr"."area" IN (700) AND "pr"."deleted_by_user"=0 AND "pr"."temporary" = 0

Navnene på objekterne er blevet ændret.

Det mest slående var, at det samme bord blev brugt flere gange, og antallet af parenteser gjorde mig simpelthen sindssyg. Jeg var ikke den eneste, der ikke kunne lide denne kode, SQL Server satte ikke så godt pris på den og brugte mange ressourcer på at bygge en plan for den. Forespørgslen kan køre i 50-150 ms, og planoprettelsen kan tage op til 2,5 ms. I dag vil jeg ikke overveje måder at løse problemet på, men jeg vil fortælle én ting – i mit tilfælde var det umuligt at rette forespørgselsgenerering i applikationen.

I stedet vil jeg gerne analysere årsagerne til, at SQL Server bygger forespørgselsplanen så længe. I enhver DBMS, inklusive SQL Server, er det vigtigste optimeringsproblem metoden til at forbinde tabeller med hinanden. Udover join-metoden er rækkefølgen af ​​tabel joins meget vigtig.

Lad os tale om rækkefølgen af ​​tabelsammenføjninger i detaljer. Det er meget vigtigt at forstå, at det mulige antal tabelforbindelser vokser eksponentielt, ikke lineært. Fox eksempel, der er kun 2 mulige metoder til at forbinde 2 tabeller, og antallet kan nå 12 metoder for 3 tabeller. Forskellige joinsekvenser kan have forskellige forespørgselsomkostninger, og SQL Server Optimizer skal vælge den mest optimale metode. Men når antallet af borde er højt, bliver det en ressourcekrævende opgave. Hvis SQL Server begynder at gennemgå alle mulige varianter, vil en sådan forespørgsel muligvis aldrig blive udført. Det er derfor, SQL Server aldrig gør det og leder altid efter en ganske god plan, ikke den bedste plan. SQL Server forsøger altid at nå et kompromis mellem eksekveringstid og plankvalitet.

Her er et eksempel på den eksponentielle vækst af joinmetoder. SQL Server kan vælge forskellige joinmetoder (venstre-dyb, højre-dyb, buskede træer). Visuelt ser det ud på følgende måde:

Tabellen nedenfor viser de mulige joinmetoder, når antallet af tabeller stiger:

Du kan få disse værdier på egen hånd:

For left-deep: 5! =5 x 4 x 3 x 2 x 1 =120

For busket træ: (2n–2)!/(n–1)!

Konklusion :Vær særlig opmærksom på antallet af JOINs og gå ikke i vejen med optimizer. Hvis du ikke får det ønskede resultat i forespørgslen, der indeholder flere JOINs, skal du dele den op i flere små forespørgsler, og du vil blive overrasket over resultatet.

P.S. Selvfølgelig skal vi forstå, at udover at definere en sekvens af tabelsammenføjninger, skal forespørgselsoptimeringsværktøjet også vælge jointype, dataadgangsmetoden (Scan, Seek) osv.

Nyttige produkter:

SQL Complete – skriv, forskønne, refaktorér din kode nemt og øg din produktivitet.


  1. Forskellen mellem inline og out-of-line begrænsninger

  2. 12 MySQL/MariaDB Sikkerhed bedste praksis for Linux

  3. Hvad er Sequence i oracle

  4. Top 10 metoder til at forbedre ETL-ydeevne ved hjælp af SSIS