14.3. УпÑавление планиÑовÑиком Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ ÑвнÑÑ
пÑедложений JOIN #
Ðоведением планиÑовÑика в некоÑоÑой ÑÑепени можно ÑпÑавлÑÑÑ, иÑполÑзÑÑ ÑвнÑй ÑинÑакÑÐ¸Ñ JOIN. ÐонÑÑÑ, когда и поÑÐµÐ¼Ñ ÑÑо бÑÐ²Ð°ÐµÑ Ð½Ñжно, Ð¿Ð¾Ð¼Ð¾Ð¶ÐµÑ Ð½ÐµÐ±Ð¾Ð»ÑÑое введение.
РпÑоÑÑом запÑоÑе Ñ Ñоединением, напÑÐ¸Ð¼ÐµÑ Ñаком:
SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id;
планиÑовÑик Ð¼Ð¾Ð¶ÐµÑ ÑоединÑÑÑ Ð´Ð°Ð½Ð½Ñе ÑаблиÑÑ Ð² лÑбом поÑÑдке. ÐапÑимеÑ, он Ð¼Ð¾Ð¶ÐµÑ ÑазÑабоÑаÑÑ Ð¿Ð»Ð°Ð½, в коÑоÑом ÑнаÑала A ÑоединÑеÑÑÑ Ñ B по ÑÑÐ»Ð¾Ð²Ð¸Ñ WHERE a.id = b.id, а заÑем C ÑоединÑеÑÑÑ Ñ Ð¿Ð¾Ð»ÑÑивÑейÑÑ ÑаблиÑей по дÑÑÐ³Ð¾Ð¼Ñ ÑÑÐ»Ð¾Ð²Ð¸Ñ WHERE. Ðибо он Ð¼Ð¾Ð¶ÐµÑ ÑоединиÑÑ B Ñ C, а заÑем Ñ A ÑезÑлÑÑаÑом ÑоединениÑ. Ðн Ñакже Ð¼Ð¾Ð¶ÐµÑ ÑоединиÑÑ ÑнаÑала A Ñ C, а заÑем ÑезÑлÑÑÐ°Ñ Ñ B â но ÑÑо бÑÐ´ÐµÑ Ð½Ðµ ÑÑÑекÑивно, Ñак как ÐµÐ¼Ñ Ð¿ÑидÑÑÑÑ ÑÑоÑмиÑоваÑÑ Ð¿Ð¾Ð»Ð½Ð¾Ðµ декаÑÑово пÑоизведение A и C из-за оÑÑÑÑÑÑÐ²Ð¸Ñ Ð² пÑедложении WHERE ÑÑловиÑ, подÑ
одÑÑего Ð´Ð»Ñ Ð¾Ð¿ÑимизаÑии ÑоединениÑ. (Ð Postgres Pro иÑполниÑÐµÐ»Ñ Ð·Ð°Ð¿ÑоÑов Ð¼Ð¾Ð¶ÐµÑ ÑоединÑÑÑ ÑолÑко по две ÑаблиÑÑ, поÑÑÐ¾Ð¼Ñ Ð´Ð»Ñ Ð¿Ð¾Ð»ÑÑÐµÐ½Ð¸Ñ ÑезÑлÑÑаÑа нÑжно вÑбÑаÑÑ Ð¾Ð´Ð¸Ð½ из ÑÑиÑ
ÑпоÑобов.) ÐÑи ÑÑом важно понимаÑÑ, ÑÑо вÑе ÑÑи ÑазнÑе ÑпоÑÐ¾Ð±Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð´Ð°ÑÑ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñе по ÑмÑÑÐ»Ñ ÑезÑлÑÑаÑÑ, но ÑÑоимоÑÑÑ Ð¸Ñ
Ð¼Ð¾Ð¶ÐµÑ ÑазлиÑаÑÑÑÑ Ð¼Ð½Ð¾Ð³Ð¾ÐºÑаÑно. ÐоÑÑÐ¾Ð¼Ñ Ð¿Ð»Ð°Ð½Ð¸ÑовÑик должен изÑÑиÑÑ Ð¸Ñ
вÑе и найÑи ÑамÑй ÑÑÑекÑивнÑй ÑпоÑоб вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿ÑоÑа.
Ðогда запÑÐ¾Ñ Ð²ÐºÐ»ÑÑÐ°ÐµÑ ÑолÑко две или ÑÑи ÑаблиÑÑ, Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ñ Ð²Ñего неÑколÑко ваÑианÑов Ð¸Ñ ÑоединениÑ. Ðо Ð¸Ñ ÑиÑло ÑаÑÑÑÑ ÑкÑпоненÑиалÑно Ñ ÑвелиÑением ÑиÑла задейÑÑвованнÑÑ ÑаблиÑ. ÐÑли ÑиÑло ÑÐ°Ð±Ð»Ð¸Ñ Ð±Ð¾Ð»ÑÑе деÑÑÑи, Ñже пÑакÑиÑеÑки невозможно вÑполниÑÑ Ð¿Ð¾Ð»Ð½Ñй пеÑÐµÐ±Ð¾Ñ Ð²ÑÐµÑ Ð²Ð°ÑианÑов, и даже Ð´Ð»Ñ ÑеÑÑи или Ñеми ÑÐ°Ð±Ð»Ð¸Ñ Ð¿Ð»Ð°Ð½Ð¸Ñование Ð¼Ð¾Ð¶ÐµÑ Ð·Ð°Ð½ÑÑÑ Ð½ÐµÐ´Ð¾Ð¿ÑÑÑимо много вÑемени. Ðогда ÑÐ°Ð±Ð»Ð¸Ñ ÑлиÑком много, планиÑовÑик Postgres Pro пеÑеклÑÑаеÑÑÑ Ñ Ð¿Ð¾Ð»Ð½Ð¾Ð³Ð¾ поиÑка на алгоÑиÑм генеÑиÑеÑкого веÑоÑÑноÑÑного поиÑка в огÑаниÑенном ÑиÑле ваÑианÑов. (ÐоÑог Ð´Ð»Ñ ÑÑого пеÑеклÑÑÐµÐ½Ð¸Ñ Ð·Ð°Ð´Ð°ÑÑÑÑ Ð¿Ð°ÑамеÑÑом вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ geqo_threshold.) ÐенеÑиÑеÑкий поиÑк вÑполнÑеÑÑÑ Ð±ÑÑÑÑее, но не гаÑанÑиÑÑеÑ, ÑÑо найденнÑй план бÑÐ´ÐµÑ Ð½Ð°Ð¸Ð»ÑÑÑим.
Ðогда запÑÐ¾Ñ Ð²ÐºÐ»ÑÑÐ°ÐµÑ Ð²Ð½ÐµÑние ÑоединениÑ, планиÑовÑик Ð¸Ð¼ÐµÐµÑ Ð¼ÐµÐ½ÑÑе ÑÑепеней ÑвободÑ, Ñем Ñ Ð¾Ð±ÑÑнÑми (внÑÑÑенними) ÑоединениÑми. ÐапÑимеÑ, ÑаÑÑмоÑÑим запÑоÑ:
SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
ХоÑÑ Ð¾Ð³ÑаниÑÐµÐ½Ð¸Ñ Ð² ÑÑом запÑоÑе оÑÐµÐ½Ñ Ð¿Ð¾Ñ Ð¾Ð¶Ð¸ на показаннÑе в пÑедÑдÑÑем пÑимеÑе, ÑмÑÑл его оÑлиÑаеÑÑÑ, Ñак как ÑезÑлÑÑиÑÑÑÑÐ°Ñ ÑÑÑока должна вÑдаваÑÑÑÑ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ ÑÑÑоки A, даже еÑли Ð´Ð»Ñ Ð½ÐµÑ Ð½Ðµ Ð½Ð°Ñ Ð¾Ð´Ð¸ÑÑÑ ÑооÑвеÑÑÑÐ²Ð¸Ñ Ð² Ñоединении B и C. Таким обÑазом, здеÑÑ Ð¿Ð»Ð°Ð½Ð¸ÑовÑик не Ð¼Ð¾Ð¶ÐµÑ Ð²ÑбиÑаÑÑ Ð¿Ð¾ÑÑдок ÑоединениÑ: он должен ÑоединиÑÑ B Ñ C, а заÑем ÑоединиÑÑ A Ñ ÑезÑлÑÑаÑом. СооÑвеÑÑÑвенно, и план ÑÑого запÑоÑа поÑÑÑоиÑÑÑ Ð±ÑÑÑÑее, Ñем пÑедÑдÑÑего. РдÑÑÐ³Ð¸Ñ ÑлÑÑаÑÑ Ð¿Ð»Ð°Ð½Ð¸ÑовÑик ÑÐ¼Ð¾Ð¶ÐµÑ Ð¾Ð¿ÑеделиÑÑ, ÑÑо можно безопаÑно вÑбÑаÑÑ Ð¾Ð´Ð¸Ð½ из неÑколÑÐºÐ¸Ñ ÑпоÑобов ÑоединениÑ. ÐапÑимеÑ, Ð´Ð»Ñ Ð·Ð°Ð¿ÑоÑа:
SELECT * FROM a LEFT JOIN b ON (a.bid = b.id) LEFT JOIN c ON (a.cid = c.id);
можно ÑоединиÑÑ A либо Ñ B, либо Ñ C. РнаÑÑоÑÑее вÑÐµÐ¼Ñ ÑолÑко FULL JOIN полноÑÑÑÑ Ð¾Ð³ÑаниÑÐ¸Ð²Ð°ÐµÑ Ð¿Ð¾ÑÑдок ÑоединениÑ. Ðа пÑакÑике в болÑÑинÑÑве запÑоÑов Ñ LEFT JOIN и RIGHT JOIN поÑÑдком можно ÑпÑавлÑÑÑ Ð² некоÑоÑой ÑÑепени.
СинÑакÑÐ¸Ñ Ñвного внÑÑÑеннего ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (INNER JOIN, CROSS JOIN или лакониÑнÑй JOIN) по ÑмÑÑÐ»Ñ ÑавнознаÑен пеÑеÑиÑÐ»ÐµÐ½Ð¸Ñ Ð¾ÑноÑений в пÑедложении FROM, Ñак ÑÑо он никак не огÑаниÑÐ¸Ð²Ð°ÐµÑ Ð¿Ð¾ÑÑдок Ñоединений.
ХоÑÑ Ð±Ð¾Ð»ÑÑинÑÑво видов JOIN не полноÑÑÑÑ Ð¾Ð³ÑаниÑиваÑÑ Ð¿Ð¾ÑÑдок ÑоединениÑ, в Postgres Pro можно пÑинÑдиÑÑ Ð¿Ð»Ð°Ð½Ð¸ÑовÑик обÑабаÑÑваÑÑ Ð²Ñе пÑÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ JOIN как огÑаниÑиваÑÑие ÑÑÐ¾Ñ Ð¿Ð¾ÑÑдок. ÐапÑимеÑ, ÑледÑÑÑие ÑÑи запÑоÑа логиÑеÑки ÑавнознаÑнÑ:
SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id; SELECT * FROM a CROSS JOIN b CROSS JOIN c WHERE a.id = b.id AND b.ref = c.id; SELECT * FROM a JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
Ðо еÑли Ð¼Ñ Ñкажем планиÑовÑÐ¸ÐºÑ ÑоблÑдаÑÑ Ð¿Ð¾ÑÑдок JOIN, на планиÑование вÑоÑого и ÑÑеÑÑего ÑйдÑÑ Ð¼ÐµÐ½ÑÑе вÑемени. Ðогда ÑеÑÑ Ð¸Ð´ÑÑ ÑолÑко о ÑÑÑÑ
ÑаблиÑаÑ
, вÑигÑÑÑ Ð±ÑÐ´ÐµÑ Ð½ÐµÐ·Ð½Ð°ÑиÑелÑнÑм, но Ð´Ð»Ñ Ð¼Ð½Ð¾Ð¶ÐµÑÑва ÑÐ°Ð±Ð»Ð¸Ñ ÑÑо Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¾ÑÐµÐ½Ñ ÑÑÑекÑивно.
ЧÑÐ¾Ð±Ñ Ð¿Ð»Ð°Ð½Ð¸ÑовÑик ÑоблÑдал поÑÑдок внÑÑÑеннего ÑоединениÑ, вÑÑаженнÑй Ñвно пÑедложениÑми JOIN, нÑжно пÑиÑвоиÑÑ Ð¿Ð°ÑамеÑÑÑ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ join_collapse_limit знаÑение 1. (ÐÑÑгие допÑÑÑимÑе знаÑÐµÐ½Ð¸Ñ Ð¾Ð±ÑÑждаÑÑÑÑ Ð½Ð¸Ð¶Ðµ.)
ЧÑÐ¾Ð±Ñ ÑокÑаÑиÑÑ Ð²ÑÐµÐ¼Ñ Ð¿Ð¾Ð¸Ñка, необÑзаÑелÑно полноÑÑÑÑ Ð¾Ð³ÑаниÑиваÑÑ Ð¿Ð¾ÑÑдок Ñоединений, в JOIN можно ÑоединÑÑÑ ÑлеменÑÑ ÐºÐ°Ðº в обÑÑном ÑпиÑке FROM. ÐапÑимеÑ, ÑаÑÑмоÑÑиÑе ÑледÑÑÑий запÑоÑ:
SELECT * FROM a CROSS JOIN b, c, d, e WHERE ...;
ÐÑли join_collapse_limit = 1, планиÑовÑик бÑÐ´ÐµÑ Ð²ÑнÑжден ÑоединиÑÑ A Ñ B ÑанÑÑе, Ñем ÑезÑлÑÑÐ°Ñ Ñ Ð´ÑÑгими ÑаблиÑами, но в далÑнейÑем вÑбоÑе ваÑианÑов он не огÑаниÑен. Рданном пÑимеÑе ÑиÑло возможнÑÑ
ваÑианÑов ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ ÑменÑÑаеÑÑÑ Ð² 5 Ñаз.
УпÑоÑаÑÑ Ð´Ð»Ñ Ð¿Ð»Ð°Ð½Ð¸ÑовÑика задаÑÑ Ð¿ÐµÑебоÑа ваÑианÑов Ñаким ÑпоÑобом â ÑÑо полезнÑй пÑиÑм, помогаÑÑий не ÑолÑко вÑбÑаÑÑ ÑокÑаÑиÑÑ Ð²ÑÐµÐ¼Ñ Ð¿Ð»Ð°Ð½Ð¸ÑованиÑ, но и подÑолкнÑÑÑ Ð¿Ð»Ð°Ð½Ð¸ÑовÑик к Ñ
оÑоÑÐµÐ¼Ñ Ð¿Ð»Ð°Ð½Ñ. ÐÑли планиÑовÑик по ÑмолÑÐ°Ð½Ð¸Ñ Ð²ÑбиÑÐ°ÐµÑ Ð½ÐµÑдаÑнÑй поÑÑдок ÑоединениÑ, Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе заÑÑавиÑÑ ÐµÐ³Ð¾ вÑбÑаÑÑ Ð»ÑÑÑий, пÑименив ÑинÑакÑÐ¸Ñ JOIN, конеÑно еÑли Ð²Ñ Ñами его знаеÑе. ÐÑÑÐµÐºÑ Ð¿Ð¾Ð´Ð¾Ð±Ð½Ð¾Ð¹ опÑимизаÑии ÑекомендÑеÑÑÑ Ð¿Ð¾Ð´ÑвеÑждаÑÑ ÑкÑпеÑименÑалÑно.
Ðа вÑÐµÐ¼Ñ Ð¿Ð»Ð°Ð½Ð¸ÑÐ¾Ð²Ð°Ð½Ð¸Ñ Ð²Ð»Ð¸ÑÐµÑ Ð¸ дÑÑгой, ÑеÑно ÑвÑзаннÑй ÑакÑÐ¾Ñ â ÑеÑение о вклÑÑении подзапÑоÑов в ÑодиÑелÑÑкий запÑоÑ. ÐÑÐ¸Ð¼ÐµÑ Ñакого запÑоÑа:
SELECT *
FROM x, y,
(SELECT * FROM a, b, c WHERE something) AS ss
WHERE somethingelse; Ð¢Ð°ÐºÐ°Ñ Ð¶Ðµ ÑиÑÑаÑÐ¸Ñ Ð¼Ð¾Ð¶ÐµÑ Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½ÑÑÑ Ñ Ð¿ÑедÑÑавлением, ÑодеÑжаÑим Ñоединение; вмеÑÑо ÑÑÑлки на ÑÑо пÑедÑÑавление бÑÐ´ÐµÑ Ð²ÑÑавлено его вÑÑажение SELECT и в ÑезÑлÑÑаÑе полÑÑиÑÑÑ Ð·Ð°Ð¿ÑоÑ, поÑ
ожий на показаннÑй вÑÑе. ÐбÑÑно планиÑовÑик ÑÑаÑаеÑÑÑ Ð²ÐºÐ»ÑÑиÑÑ Ð¿Ð¾Ð´Ð·Ð°Ð¿ÑÐ¾Ñ Ð² ÑодиÑелÑÑкий запÑÐ¾Ñ Ð¸ полÑÑиÑÑ Ñаким обÑазом:
SELECT * FROM x, y, a, b, c WHERE something AND somethingelse;
ЧаÑÑо ÑÑо позволÑÐµÑ Ð¿Ð¾ÑÑÑоиÑÑ Ð»ÑÑÑий план, Ñем пÑи планиÑовании подзапÑоÑов по оÑделÑноÑÑи. (ÐапÑимеÑ, внеÑние ÑÑÐ»Ð¾Ð²Ð¸Ñ WHERE могÑÑ Ð±ÑÑÑ ÑаковÑ, ÑÑо пÑи Ñоединении ÑнаÑала X Ñ A бÑÐ´ÐµÑ Ð¸ÑклÑÑено множеÑÑво ÑÑÑок A, а знаÑÐ¸Ñ ÑоÑмиÑоваÑÑ Ð»Ð¾Ð³Ð¸ÑеÑкий ÑезÑлÑÑÐ°Ñ Ð¿Ð¾Ð´Ð·Ð°Ð¿ÑоÑа полноÑÑÑÑ Ð½Ðµ поÑÑебÑеÑÑÑ.) Ðо в Ñо же вÑÐµÐ¼Ñ Ñем ÑамÑм Ð¼Ñ ÑвелиÑиваем вÑÐµÐ¼Ñ Ð¿Ð»Ð°Ð½Ð¸ÑованиÑ; две задаÑи ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ ÑÑÑÑ
ÑлеменÑов Ð¼Ñ Ð·Ð°Ð¼ÐµÐ½Ñем одной Ñ Ð¿ÑÑÑÑ ÑлеменÑами. Так как ÑиÑло ваÑианÑов ÑвелиÑиваеÑÑÑ ÑкÑпоненÑиалÑно, ÑложноÑÑÑ Ð·Ð°Ð´Ð°Ñи ÑвелиÑиваеÑÑÑ Ð¼Ð½Ð¾Ð³Ð¾ÐºÑаÑно. ÐланиÑовÑик пÑÑаеÑÑÑ Ð¸Ð·Ð±ÐµÐ¶Ð°ÑÑ Ð¿Ñоблем поиÑка Ñ Ð¾Ð³ÑомнÑм ÑиÑлом ваÑианÑов, ÑаÑÑмаÑÑÐ¸Ð²Ð°Ñ Ð¿Ð¾Ð´Ð·Ð°Ð¿ÑоÑÑ Ð¾ÑделÑно, еÑли в пÑедложении FROM ÑодиÑелÑÑкого запÑоÑа оказÑваеÑÑÑ Ð±Ð¾Ð»ÑÑе Ñем from_collapse_limit ÑлеменÑов. ÐзменÑÑ ÑÑÐ¾Ñ Ð¿Ð°ÑамеÑÑ Ð²ÑполнениÑ, можно подобÑаÑÑ Ð¾Ð¿ÑималÑное ÑооÑноÑение вÑемени планиÑÐ¾Ð²Ð°Ð½Ð¸Ñ Ð¸ каÑеÑÑва плана.
ÐаÑамеÑÑÑ from_collapse_limit и join_collapse_limit назÑваÑÑÑÑ Ð¿Ð¾Ñ
оже, поÑÐ¾Ð¼Ñ ÑÑо они делаÑÑ Ð¿ÑакÑиÑеÑки одно и Ñо же: пеÑвÑй паÑамеÑÑ Ð¾Ð¿ÑеделÑеÑ, когда планиÑовÑик бÑÐ´ÐµÑ Â«ÑноÑиÑÑ» в пÑедложение FROM подзапÑоÑÑ, а вÑоÑой â ÑвнÑе ÑоединениÑ. ÐбÑÑно join_collapse_limit ÑÑÑанавливаеÑÑÑ ÑавнÑм from_collapse_limit (ÑÑÐ¾Ð±Ñ ÑвнÑе ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¸ подзапÑоÑÑ Ð¾Ð±ÑабаÑÑвалиÑÑ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ð¾) или 1 (еÑли ÑÑебÑеÑÑÑ ÑпÑавлÑÑÑ Ð¿Ð¾ÑÑдком Ñоединений). Ðо Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе задаÑÑ Ð´ÑÑгие знаÑениÑ, ÑÑÐ¾Ð±Ñ Ð´Ð¾Ð±Ð¸ÑÑÑÑ Ð¾Ð¿ÑималÑного ÑооÑноÑÐµÐ½Ð¸Ñ Ð²Ñемени планиÑÐ¾Ð²Ð°Ð½Ð¸Ñ Ð¸ вÑемени вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿ÑоÑов.