Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Wengi ambao tayari wanatumia eleza.tensor.ru - Huduma yetu ya utazamaji wa mpango wa PostgreSQL inaweza kuwa haifahamu mojawapo ya nguvu zake kuu - kubadilisha kipande cha kumbukumbu cha seva ambacho ni kigumu kusomeka...

Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja
... katika swali lililoundwa kwa uzuri na vidokezo vya muktadha kwa nodi za mpango zinazolingana:

Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja
Katika nakala hii ya sehemu yake ya pili ripoti katika PGConf.Russia 2020 Nitakuambia jinsi tuliweza kufanya hivi.

Nakala ya sehemu ya kwanza, iliyowekwa kwa shida za kawaida za utendakazi wa hoja na suluhisho zao, inaweza kupatikana katika kifungu. "Maelekezo ya maswali ya SQL mgonjwa".



Kwanza, hebu tuanze kuchorea - na hatuwezi tena rangi ya mpango, tayari tumepiga rangi, tayari tunayo nzuri na inayoeleweka, lakini ombi.

Ilionekana kwetu kuwa kwa "karatasi" isiyo na muundo kama huo ombi lililotolewa kutoka kwa logi inaonekana kuwa mbaya sana na kwa hivyo haifai.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Hasa wakati watengenezaji "gundi" mwili wa ombi katika kanuni (hii ni, bila shaka, antipattern, lakini hutokea) katika mstari mmoja. Ya kutisha!

Wacha tuchore hii kwa uzuri zaidi.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Na ikiwa tunaweza kuchora hii kwa uzuri, ambayo ni, kutenganisha na kuweka pamoja mwili wa ombi, basi tunaweza "kuambatisha" wazo kwa kila kitu cha ombi hili - kile kilichotokea katika hatua inayolingana ya mpango.

Swali la mti wa syntax

Ili kufanya hivyo, ombi lazima kwanza lichanganuliwe.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Kwa sababu tuna msingi wa mfumo unaendesha NodeJS, kisha tukaitengenezea moduli, unaweza pata kwenye GitHub. Kwa kweli, hizi ni "vifungo" vilivyopanuliwa kwa watu wa ndani wa kichanganuzi cha PostgreSQL chenyewe. Hiyo ni, sarufi imeundwa kwa njia ya binary na vifungo vinafanywa kutoka kwa NodeJS. Tulichukua moduli za watu wengine kama msingi - hakuna siri kubwa hapa.

Tunalisha mwili wa ombi kama ingizo la utendakazi wetu - kwa matokeo tunapata mti wa sintaksia uliochanganuliwa katika mfumo wa kitu cha JSON.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Sasa tunaweza kukimbia kupitia mti huu katika mwelekeo tofauti na kukusanya ombi na indents, rangi, na umbizo tunataka. Hapana, hii haiwezi kubinafsishwa, lakini ilionekana kwetu kuwa hii itakuwa rahisi.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Hoja ya ramani na nodi za kupanga

Sasa hebu tuone jinsi tunavyoweza kuchanganya mpango ambao tulichanganua katika hatua ya kwanza na swali ambalo tulichambua katika pili.

Hebu tuchukue mfano rahisi - tuna swali ambalo hutoa CTE na kusoma kutoka humo mara mbili. Anatengeneza mpango kama huo.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

CTE

Ikiwa utaiangalia kwa uangalifu, hadi toleo la 12 (au kuanzia na neno kuu MATERIALIZED) malezi CTE ni kizuizi kabisa kwa mpangaji.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Hii ina maana kwamba ikiwa tunaona kizazi cha CTE mahali fulani katika ombi na node mahali fulani katika mpango CTE, basi nodi hizi hakika "hupigana" na kila mmoja, tunaweza kuzichanganya mara moja.

Tatizo na nyota: CTE zinaweza kuwekwa.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja
Kuna viota duni sana, na hata vya majina sawa. Kwa mfano, unaweza ndani CTE A fanya CTE X, na kwa kiwango sawa ndani CTE B fanya tena CTE X:

WITH A AS (
  WITH X AS (...)
  SELECT ...
)
, B AS (
  WITH X AS (...)
  SELECT ...
)
...

Wakati wa kulinganisha, lazima uelewe hili. Kuelewa hii "kwa macho yako" - hata kuona mpango, hata kuona mwili wa ombi - ni ngumu sana. Ikiwa kizazi chako cha CTE ni ngumu, kimewekwa, na maombi ni makubwa, basi ni fahamu kabisa.

UNION

Ikiwa tunayo neno kuu katika swali UNION [ALL] (opereta wa kuunganisha sampuli mbili), basi katika mpango huo inafanana na node ama Append, au baadhi Recursive Union.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Ile "juu" hapo juu UNION - huyu ndiye mzao wa kwanza wa nodi yetu, ambayo iko "chini" - ya pili. Ikiwa kupitia UNION tuna vitalu kadhaa "glued" mara moja, basi Append-Bado kutakuwa na nodi moja tu, lakini haitakuwa na mbili, lakini watoto wengi - kwa mpangilio wanaoenda, mtawaliwa:

  (...) -- #1
UNION ALL
  (...) -- #2
UNION ALL
  (...) -- #3

Append
  -> ... #1
  -> ... #2
  -> ... #3

Tatizo na nyota: ndani ya kizazi cha sampuli inayojirudia (WITH RECURSIVE) pia inaweza kuwa zaidi ya moja UNION. Lakini kizuizi cha mwisho tu baada ya cha mwisho huwa kinajirudia kila wakati UNION. Kila kitu hapo juu ni moja, lakini tofauti UNION:

WITH RECURSIVE T AS(
  (...) -- #1
UNION ALL
  (...) -- #2, Ρ‚ΡƒΡ‚ кончаСтся гСнСрация стартового состояния рСкурсии
UNION ALL
  (...) -- #3, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ этот Π±Π»ΠΎΠΊ рСкурсивный ΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ ΠΊ T
)
...

Pia unahitaji kuwa na uwezo wa "kuweka" mifano kama hiyo. Katika mfano huu tunaona hivyo UNION-kulikuwa na sehemu 3 katika ombi letu. Ipasavyo, moja UNION inafanana na Append-nodi, na kwa nyingine - Recursive Union.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Data ya kusoma-kuandika

Kila kitu kimewekwa, sasa tunajua ni kipande gani cha ombi kinacholingana na kipande gani cha mpango. Na katika vipande hivi tunaweza kupata kwa urahisi na kwa kawaida vitu hivyo ambavyo "vinasomeka".

Kwa mtazamo wa hoja, hatujui ikiwa ni jedwali au CTE, lakini zimeteuliwa kwa nodi sawa. RangeVar. Na kwa suala la "kusoma", hii pia ni seti ndogo ya nodi:

  • Seq Scan on [tbl]
  • Bitmap Heap Scan on [tbl]
  • Index [Only] Scan [Backward] using [idx] on [tbl]
  • CTE Scan on [cte]
  • Insert/Update/Delete on [tbl]

Tunajua muundo wa mpango na swala, tunajua mawasiliano ya vitalu, tunajua majina ya vitu - tunafanya kulinganisha moja hadi moja.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Tena kazi "na nyota". Tunachukua ombi, kutekeleza, hatuna lakabu yoyote - tunaisoma mara mbili kutoka kwa CTE ile ile.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Tunaangalia mpango - shida ni nini? Kwa nini tulikuwa na alias? Hatukuiagiza. Anapata wapi "nambari" kama hiyo?

PostgreSQL inaongeza yenyewe. Unahitaji tu kuelewa hilo jina la utani kama hilo kwa ajili yetu, kwa madhumuni ya kulinganisha na mpango, haina maana yoyote, ni aliongeza tu hapa. Tusiwe makini naye.

Ya pili kazi "na nyota": ikiwa tunasoma kutoka kwa meza iliyogawanyika, basi tutapata node Append au Merge Append, ambayo itakuwa na idadi kubwa ya "watoto", na kila moja ambayo itakuwa kwa namna fulani Scan'om kutoka kwa sehemu ya jedwali: Seq Scan, Bitmap Heap Scan au Index Scan. Lakini, kwa hali yoyote, "watoto" hawa hawatakuwa maswali magumu - hivi ndivyo nodi hizi zinaweza kutofautishwa kutoka. Append saa UNION.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Pia tunaelewa mafundo kama haya, tunayakusanya "katika rundo moja" na kusema: "kila kitu unachosoma kutoka kwa megatable kiko hapa na chini ya mti".

"Rahisi" data kupokea nodi

Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Values Scan inalingana katika mpango VALUES katika ombi.

Result ni ombi bila FROM aina ya SELECT 1. Au unapokuwa na usemi wa uwongo kimakusudi WHERE-block (basi sifa inaonekana One-Time Filter):

EXPLAIN ANALYZE
SELECT * FROM pg_class WHERE FALSE; -- ΠΈΠ»ΠΈ 0 = 1

Result  (cost=0.00..0.00 rows=0 width=230) (actual time=0.000..0.000 rows=0 loops=1)
  One-Time Filter: false

Function Scan "ramani" kwa SRF za jina moja.

Lakini kwa maswali yaliyowekwa kiota kila kitu ni ngumu zaidi - kwa bahati mbaya, hazigeuki kila wakati InitPlan/SubPlan. Wakati mwingine hugeuka kuwa ... Join au ... Anti Join, hasa unapoandika kitu kama WHERE NOT EXISTS .... Na hapa si mara zote inawezekana kuchanganya nao - katika maandishi ya mpango hakuna waendeshaji sambamba na nodes za mpango.

Tena kazi "na nyota": baadhi VALUES katika ombi. Katika kesi hii na katika mpango utapata nodes kadhaa Values Scan.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Viambishi vya "Hesabu" vitasaidia kutofautisha kutoka kwa kila mmoja - huongezwa haswa kwa mpangilio ambao sawa hupatikana. VALUES-vizuizi pamoja na ombi kutoka juu hadi chini.

Usindikaji wa data

Inaonekana kila kitu katika ombi letu kimetatuliwa - kilichosalia ni Limit.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Lakini hapa kila kitu ni rahisi - nodi kama vile Limit, Sort, Aggregate, WindowAgg, Unique "ramani" moja kwa moja kwa waendeshaji sambamba katika ombi, ikiwa wapo. Hakuna "nyota" au shida hapa.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

JOIN

Ugumu hutokea tunapotaka kuchanganya JOIN kati yao wenyewe. Hii haiwezekani kila wakati, lakini inawezekana.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Kwa mtazamo wa mchanganuzi wa hoja, tunayo nodi JoinExpr, ambayo ina watoto wawili hasa - kushoto na kulia. Hiki, ipasavyo, ndicho kilicho "juu" ya KUJIUNGA kwako na kile kilichoandikwa "chini" yake katika ombi.

Na kwa mtazamo wa mpango, hawa ni wazao wawili wa baadhi * Loop/* Join-nodi. Nested Loop, Hash Anti Join,... - kitu kama hicho.

Wacha tutumie mantiki rahisi: ikiwa tuna meza A na B ambazo "zinajiunga" katika mpango, basi katika ombi zinaweza kupatikana ama. A-JOIN-BAu B-JOIN-A. Hebu jaribu kuchanganya kwa njia hii, hebu tujaribu kuchanganya njia nyingine kote, na kadhalika mpaka tutakapokwisha jozi hizo.

Wacha tuchukue mti wetu wa syntax, tuchukue mpango wetu, tuwaangalie ... sio sawa!
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Wacha tuichore tena kwa namna ya grafu - oh, tayari inaonekana kama kitu!
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Hebu tukumbuke kwamba tuna nodes ambazo wakati huo huo zina watoto B na C - hatujali kwa utaratibu gani. Wacha tuwachanganye na tugeuze picha ya nodi.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Hebu tuangalie tena. Sasa tuna nodi na watoto A na jozi (B + C) - zinazoendana nao pia.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Kubwa! Inageuka kuwa sisi ni hawa wawili JOIN kutoka kwa ombi na nodi za mpango ziliunganishwa kwa mafanikio.

Ole, tatizo hili si mara zote kutatuliwa.
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Kwa mfano, ikiwa katika ombi A JOIN B JOIN C, na katika mpango huo, kwanza kabisa, nodes za "nje" A na C ziliunganishwa. Lakini hakuna operator vile katika ombi, hatuna chochote cha kuonyesha, hakuna kitu cha kuunganisha kidokezo. Ni sawa na "comma" unapoandika A, B.

Lakini, katika hali nyingi, karibu nodi zote zinaweza "kufunguliwa" na unaweza kupata aina hii ya wasifu upande wa kushoto kwa wakati - kihalisi, kama kwenye Google Chrome unapochambua msimbo wa JavaScript. Unaweza kuona ni muda gani kila mstari na kila taarifa ilichukua "kutekeleza."
Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Na ili iwe rahisi kwako kutumia haya yote, tumehifadhi kumbukumbu, ambapo unaweza kuhifadhi na baadaye kupata mipango yako pamoja na maombi yanayohusiana au kushiriki kiungo na mtu fulani.

Ikiwa unahitaji tu kuleta swali lisiloweza kusoma katika fomu ya kutosha, tumia "kawaida" wetu.

Profaili ya Swali la PostgreSQL: jinsi ya kulinganisha mpango na hoja

Chanzo: mapenzi.com

Kuongeza maoni