Onlangs kwam ik een toepassing tegen die DB-query's genereerde. Ik begrijp dat daar niets nieuws aan is, maar toen de applicatie langzaam begon te werken en ik de reden van de vertraging moest achterhalen, was ik verbaasd om deze vragen te vinden. Dit is waar SQL Server soms mee te maken heeft:
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
De namen van de objecten zijn gewijzigd.
Het meest opvallende was dat dezelfde tabel meerdere keren werd gebruikt, en het aantal haakjes maakte me gewoon gek. Ik was niet de enige die deze code niet leuk vond, SQL Server stelde het niet zo goed op prijs en besteedde veel middelen om er een plan voor te maken. De query kan 50-150 ms duren en het maken van een plan kan tot 2,5 ms duren. Vandaag zal ik niet ingaan op de manieren om het probleem op te lossen, maar ik zal één ding zeggen:in mijn geval was het onmogelijk om het genereren van query's in de toepassing op te lossen.
In plaats daarvan zou ik de redenen willen analyseren waarom SQL Server het queryplan zo lang bouwt. In elk DBMS, inclusief SQL Server, is het belangrijkste optimalisatieprobleem de methode om tabellen aan elkaar te koppelen. Naast de join-methode is de volgorde van de table-joins erg belangrijk.
Laten we het in detail hebben over de volgorde van tafel-joins. Het is erg belangrijk om te begrijpen dat het mogelijke aantal tabeljoins exponentieel groeit, niet lineair. Fox bijvoorbeeld, er zijn slechts 2 mogelijke methoden om 2 tabellen samen te voegen, en het aantal kan oplopen tot 12 methoden voor 3 tabellen. Verschillende samenvoegreeksen kunnen verschillende querykosten hebben, en SQL Server Optimizer moet de meest optimale methode selecteren. Maar wanneer het aantal tafels hoog is, wordt het een arbeidsintensieve taak. Als SQL Server alle mogelijke varianten gaat doornemen, mag een dergelijke query nooit worden uitgevoerd. Dat is de reden waarom SQL Server het nooit doet en altijd zoekt naar een redelijk goed plan, niet het beste plan. SQL Server probeert altijd een compromis te bereiken tussen uitvoeringstijd en plankwaliteit.
Hier is een voorbeeld van de exponentiële groei van join-methoden. SQL Server kan verschillende join-methoden selecteren (links-diep, rechts-diep, bossige bomen). Visueel ziet het er als volgt uit:
De onderstaande tabel toont de mogelijke join-methoden wanneer het aantal tafels toeneemt:
U kunt deze waarden zelf verkrijgen:
Voor links-diep: 5! =5 x 4 x 3 x 2 x 1 =120
Voor bosachtige boom: (2n–2)!/(n–1)!
Conclusie :Let vooral op het aantal JOIN's en sta niet in de weg met optimizer. Als u niet het gewenste resultaat krijgt in de query die meerdere JOIN's bevat, breek deze dan op in verschillende kleine query's en u zult verrast zijn met het resultaat.
P.S. Natuurlijk moeten we begrijpen dat naast het definiëren van een reeks tabel-joins, de query-optimizer ook het join-type, de gegevenstoegangsmethode (Scannen, zoeken) enz. moet selecteren.
Handige producten:
SQL Compleet - schrijf, verfraai, refactor uw code eenvoudig en verhoog uw productiviteit.