Database
 sql >> база данни >  >> RDS >> Database

Защо множеството JOIN са лоши за заявка или не пречат на оптимизатора

Наскоро попаднах на приложение, което генерира DB заявки. Разбирам, че няма нищо ново в това, но когато приложението започна да работи бавно и трябваше да разбера причината за забавянето, бях изумен, когато открих тези заявки. Ето с какво SQL Server понякога трябва да се справя:

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

Имената на обектите са променени.

Най-поразителното беше, че една и съща таблица беше използвана няколко пъти, а броят на скоби просто ме побърка. Не бях единственият, който не хареса този код, SQL Server също не го оцени и изразходва много ресурси за изграждане на план за него. Заявката може да се изпълнява за 50-150 ms, а създаването на план може да отнеме до 2,5 ms. Днес няма да разглеждам начините за отстраняване на проблема, но ще кажа едно – в моя случай беше невъзможно да се коригира генерирането на заявки в приложението.

Вместо това бих искал да анализирам причините, поради които SQL Server изгражда плана на заявката толкова дълго. Във всяка СУБД, включително SQL Sever, основният проблем при оптимизиране е методът за свързване на таблици една с друга. Освен метода на присъединяване, последователността на присъединяването на таблици е много важна.

Нека поговорим подробно за последователността на присъединяването на таблици. Много е важно да се разбере, че възможният брой присъединявания на таблица нараства експоненциално, а не линейно. Например Fox, има само 2 възможни метода за свързване на 2 таблици, а броят може да достигне 12 метода за 3 таблици. Различните последователности на присъединяване могат да имат различна цена на заявката и оптимизаторът на SQL Server трябва да избере най-оптималния метод. Но когато броят на таблиците е голям, това се превръща в ресурсоемка задача. Ако SQL Server започне да разглежда всички възможни варианти, такава заявка може никога да не бъде изпълнена. Ето защо SQL Server никога не го прави и винаги търси доста добър, а не най-добрия план. SQL Server винаги се опитва да постигне компромис между времето за изпълнение и качеството на плана.

Ето пример за експоненциален растеж на методите за присъединяване. SQL Server може да избира различни методи за присъединяване (ляво-дълбоко, дясно-дълбоко, храстовидни дървета). Визуално изглежда по следния начин:

Таблицата по-долу показва възможните методи за присъединяване, когато броят на таблиците се увеличи:

Можете да получите тези стойности сами:

За вляво-дълбоко: 5! =5 x 4 x 3 x 2 x 1 =120

За храстовидно дърво: (2n–2)!/(n–1)!

Заключение :Обърнете специално внимание на броя на JOIN и не пречите на оптимизатора. Ако не успеете да получите желания резултат в заявката, съдържаща множество JOIN, разбийте я на няколко малки заявки и ще бъдете изненадани от резултата.

P.S. Разбира се, трябва да разберем, че освен да дефинира последователност от присъединявания на таблици, оптимизаторът на заявки трябва също да избере типа на присъединяване, метода за достъп до данни (сканиране, търсене) и т.н.

Полезни продукти:

SQL Complete – пишете, разкрасявайте, преобразувайте кода си лесно и увеличете производителността си.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Копаене по-дълбоко в миграциите на Django

  2. Подобрена поддръжка за възстановяване на паралелни статистически данни

  3. Мониторинг на архивиране в различни инстанции

  4. Профилиране на данни:Откриване на подробности за данните

  5. Миграция на схема:Връзка със звезда