ShumĂ« nga ata qĂ« tashmĂ« po e pĂ«rdorin â shĂ«rbimin tonĂ« tĂ« vizualizimit tĂ« planeve PostgreSQL, ndoshta nuk e dinĂ« njĂ« nga superfuqitĂ« e tij â tĂ« shndĂ«rrojĂ« njĂ« pjesĂ« tĂ« logut tĂ« serverit qĂ« Ă«shtĂ« e vĂ«shtirĂ« pĂ«r t'u lexuar...

... në një kërkesë të përgatitur bukur me këshilla kontekstuale për nyjet përkatëse të planit:

Në këtë interpretim të pjesës së dytë të raportit tim unë do të flas për mënyrën si arritëm ta bëjmë këtë.
Me transkriptin e pjesës së parë, e cila ishte e dedikuar problemeve tipike të performancës së kërkesave dhe zgjidhjeve të tyre, mund të njiheni në artikullin .

Fillimisht do tĂ« merremi me ngjyrat â dhe do tĂ« ngjyrosim jo planin, sepse ne e kemi ngjyrosur atĂ«, tashmĂ« Ă«shtĂ« i bukur dhe i kuptueshĂ«m, por do tĂ« merremi me kĂ«rkesĂ«n.
Na dukej se njĂ« «tepih» i tillĂ« i paformatizuar qĂ« Ă«shtĂ« nxjerrĂ« nga logu duket shumĂ« i shĂ«mtuar dhe prandaj â i pakĂ«ndshĂ«m.

Veçanërisht kur zhvilluesit në kod «bashkojnë» trupin e kërkesës (kjo, sigurisht, është një antipattern, por ndodh) në një rresht. Tmerre!
Le ta vizatojmë këtë më bukur.

NĂ«se arrijmĂ« ta vizatojmĂ« bukur kĂ«tĂ«, domethĂ«nĂ«, tĂ« analizojmĂ« dhe riparojmĂ« trupin e pyetjes, atĂ«herĂ« mund tĂ« ngjitim njĂ« sugjerim pĂ«r çdo objekt tĂ« kĂ«saj pyetjeje â se çfarĂ« ndodhi nĂ« pikĂ«n pĂ«rkatĂ«se tĂ« planit.
Pema sintaksore e pyetjes
Për ta bërë këtë, së pari duhet të analizojmë pyetjen.

Duke qenĂ« se ne , kemi krijuar njĂ« modul pĂ«r tĂ«, mund ta . NĂ« tĂ« vĂ«rtetĂ«, kjo janĂ« binding tĂ« zgjeruara pĂ«r brendĂ«sitĂ« e parserit tĂ« PostgreSQL. DomethĂ«nĂ«, Ă«shtĂ« thjesht njĂ« gramatikĂ« e pĂ«rkthyer nĂ« format binar dhe janĂ« krijuar binding me NodeJS. Ne pĂ«rdorĂ«m module tĂ« huaja si bazĂ« â nuk ka asnjĂ« sekret tĂ« madh kĂ«tu.
Futur trupin e pyetjes nĂ« funksionin tonĂ« â nĂ« daljen marrim pemĂ«n sintaksore tĂ« analizuara nĂ« formĂ«n e njĂ« objekti JSON.

Tani, mbi këtë pemë mund të kalojmë në anën tjetër dhe të rindërtojmë pyetjen me ato hapësira, ngjyrimin, formatimin që dëshirojmë. Jo, kjo nuk është e konfiguruar, por na duket se kështu do të ishte e përshtatshme.

Krahasimi i nyjeve të pyetjes dhe planit
Tani do ta shohim se si mund të kombinojmë planin që ne e shqyrtuam në hapin e parë dhe kërkesën që ne e shqyrtuam në hapin e dytë.
Le tĂ« marrim njĂ« shembull tĂ« thjeshtĂ« â kemi njĂ« kĂ«rkesĂ« qĂ« formon CTE dhe e lexon dy herĂ« nga ajo. Ai gjeneron njĂ« plan tĂ« tillĂ«.

CTE
Nëse i hedhim një vështrim të afërt, që nga versioni 12 (ose duke filluar nga ai me fjalën kyçe MATERIALIZED) formimi .

Pra, nëse shohim diku në kërkesë gjenerimin e CTE dhe diku në plan një nyje CTE, atëherë këto nyje ndërlidhen me njëra-tjetrën dhe ne mund t'i kombinojmë ato menjëherë.
Detyra "me yll": CTE mund të jenë të ngjitura.

Janë disa që janë shumë keq të ngjitura, madje edhe të emërtuara njësoj. Për shembull, ju mund të ndani CTE A të bëni CTE X, dhe në të njëjtin nivel brenda CTE B të bëni përsëri CTE X:
ME A SI (
ME X SI (...)
Zgjedh ...
)
, B SI (
ME X SI (...)
Zgjedh ...
)
...Kur krahasoni, duhet ta kuptoni kĂ«tĂ«. Ta kuptoni me "sytĂ«" â edhe duke parĂ« planin, edhe duke parĂ« trupin e kĂ«rkesĂ«s â Ă«shtĂ« shumĂ« e vĂ«shtirĂ«. NĂ«se keni njĂ« gjenerim tĂ« CTE tĂ« ndĂ«rlikuar, tĂ« ngjitur, kĂ«rkesat janĂ« tĂ« mĂ«dha â atĂ«herĂ« ndjenja Ă«shtĂ« krejtĂ«sisht e pavetĂ«dijshme.
UNION
Nëse kemi një fjalë kyçe në kërkesë UNION [ALL] (operatori i lidhjes midis dy grupeve), atëherë ai në plan i korrespondon ose një nyje Shto, ose ndonjë Bashkimi Rekursiv.

Ajo qĂ« "sipĂ«r" mbi UNION â Ă«shtĂ« pasardhĂ«si i parĂ« i nyjĂ«s tonĂ«, ndĂ«rsa "poshtĂ«" â i dyti. NĂ«se pĂ«rmes UNION kemi "nĂ« ngjitje" disa blloqe menjĂ«herĂ«, atĂ«herĂ« Shto-nyja do tĂ« mbetet vetĂ«m njĂ«, por fĂ«mijĂ«t e saj do tĂ« jenĂ« jo dy, por shumĂ« â nĂ« rendin siç vijnĂ«, pĂ«rkatĂ«sisht:
(...) -- #1
UNION ALL
(...) -- #2
UNION ALL
(...) -- #3Shto
-> ... #1
-> ... #2
-> ... #3
Detyra "me yll": brenda gjenerimit tĂ« zgjedhjes rekursive (WITH RECURSIVE) gjithashtu mund tĂ« ketĂ« mĂ« shumĂ« se njĂ« UNION. Por gjithmonĂ« rekursive Ă«shtĂ« vetĂ«m blloku mĂ« i fundit pas tĂ« fundit UNION. Ădo gjĂ« qĂ« Ă«shtĂ« lart â Ă«shtĂ« njĂ«, por tjetĂ«r UNION:
ME RECURSIVE T AS(
(...) -- #1
UNION ALL
(...) -- #2, këtu përfundon gjenerimi i gjendjes fillestare të rekursioneve
UNION ALL
(...) -- #3, vetëm ky bllok është rekursiv dhe mund të përmbajë referenca te T
)
... KĂ«to shembuj duhet gjithashtu tĂ« dinĂ« "tĂ« ngjiten". NĂ« kĂ«tĂ« shembull ne shohim se UNION-segmenteve nĂ« kĂ«rkesĂ«n tonĂ« ishin 3 copa. PĂ«rkatĂ«sisht, njĂ«ri UNION pĂ«rputhet Shto-nyjĂ«, ndĂ«rsa tjetri â Bashkimi Rekursiv.

Leximi-shkrimi i të dhënave
Të gjitha, e organizuam, tani e dimë se cili pjesë e kërkesës i përket cilit pjesë të planit. Dhe në këto pjesë ne mund të gjejmë lehtësisht objektet që "lexohen".
Nga kĂ«ndvĂ«shtrimi i kĂ«rkesĂ«s ne nuk e dimĂ« â Ă«shtĂ« tabelĂ« apo CTE, por ato karakterizohen nga e njĂ«jta nyje. RangeVar. NdĂ«rsa nĂ« planin "lexohet" â gjithashtu Ă«shtĂ« njĂ« grup mjaft tĂ« kufizuar nyjesh:
Skemë e sekuencës në [tbl]Skani i Bitmap Heap në [tbl]Indeksi [Vetëm] Skano [Mbrapsht] duke përdorur [idx] në [tbl]Skano CTE në [cte]Futni/Update/Fshi në [tbl]
Struktura e planit dhe e kĂ«rkesĂ«s e dimĂ«, pĂ«rputhja e blloqeve e dimĂ«, emrat e objekteve i dimĂ« â bĂ«jmĂ« njĂ« pĂ«rputhje tĂ« qartĂ«.

PĂ«rsĂ«ri, detyra "me yll". Marrim kĂ«rkesĂ«n, e ekzekutojmĂ«, nuk kemi asnjĂ« alias â ne thjesht lexuam dy herĂ« nga njĂ« CTE.

ShikojmĂ« nĂ« plan â çfarĂ« ndodhi? Pse na doli aliasi? Ne nuk e kĂ«rkuam. Nga erdhi ky "numer"?
PostgreSQL e shton atë vetë. Duhet thjesht të kuptojmë se pikërisht ky alias për ne për qëllimet e përputhjes me planin nuk ka asnjë kuptim, ai është thjesht këtu i shtuar. Mos ta vëmë re atë.
E dyta detyra "me yll": nëse kemi lexim nga një tabelë të ndarë, atëherë do të marrim një nyjë Shto ose Bashko Shto, e cila do të përbëhet nga një numër i madh "fëmijësh", dhe secila do të jetë një Scan' nga tabela-seksion: Skemë e sekuencës, Skani i Bitmap Heap ose Index Scan. Por megjithatë, këto "fëmijë" do të jenë kërkesa të thjeshta - kështu që këto nyje mund të dallohen nga Shto në UNION.

Këto nyje ne i kuptojmë gjithashtu, i grupojmë "në një grumbull" dhe themi: "gjithçka që ke lexuar nga megatable - është këtu dhe poshtë në pemë".
"Nyjet e thjeshta" për marrjen e të dhënave

Skano të Dhënash përputhet me VALUES në kërkesë.
Shkalla - kjo është një kërkesë pa FROM si SELECT 1. Ose kur ke një shprehje të gënjyer në WHERE-bllok (atëherë krijohet atributi One-Time Filter):
EXPLAIN ANALYZE
SELECT * FROM pg_class WHERE FALSE; -- ose 0 = 1Rezultati (cost=0.00..0.00 rows=0 width=230) (actual time=0.000..0.000 rows=0 loops=1)
One-Time Filter: false
Skano Funksional "mapojen" në SRF të njëjtë.
Por për kërkesat e brendshme gjërat janë më të komplikuara - fatkeqësisht, ato nuk transformohen gjithmonë në PlaniFill/SubPlani. Herë pas here ato kthehen në ... Join ose ... Anti Join, sidomos kur shkruani diçka si WHERE NOT EXISTS .... Dhe aty kombinimi nuk është gjithmonë i mundur - në tekstin e planit nuk ka nyje përkatëse të operatorëve.
Përsëri, detyra "me yll": disa VALUES në kërkesë. Në këtë rast, dhe në plan do të merrni disa nyje Skano të Dhënash.

Dallimi i tyre do të ndihmohet nga sufiksat "numërorë" - ai shtohet saktësisht në rendin e gjetjes së VALUES-bllokëve gjatë kërkesës nga lartë poshtë.
Përpunimi i të dhënave
Duket se kemi trajtuar gjithçka nĂ« kĂ«rkesĂ«n tonĂ« â ka mbetur vetĂ«m Kufizim.

Por kĂ«tu gjithçka Ă«shtĂ« e thjeshtĂ« â nyjet si Kufizim, Rendit, Agregoni, WindowAgg, Unike «mapohet» njĂ«-pĂ«r-njĂ« me operatorĂ«t pĂ«rkatĂ«s nĂ« kĂ«rkesĂ«, nĂ«se ata janĂ« aty. Nuk ka asnjĂ« «yll» dhe as vĂ«shtirĂ«si.

JOIN
Vështirësitë ndodhin kur duam të kombinojmë JOIN me njëri-tjetrin. Kjo nuk bëhet gjithmonë, por është e mundur.

Nga perspektiva e parserit tĂ« kĂ«rkesĂ«s, kemi njĂ« nyje BashkohuExpr, e cila ka pikĂ«risht dy pasardhĂ«s â tĂ« majtĂ« dhe tĂ« djathtĂ«. Kjo, pĂ«rkatĂ«sisht, Ă«shtĂ« ajo qĂ« Ă«shtĂ« «mbi» JOIN tuaj dhe ajo qĂ« Ă«shtĂ« «në» tĂ« shkruarit nĂ« kĂ«rkesĂ«.
Dhe nga perspektiva e planit, kjo janĂ« dy pasardhĂ«s tĂ« njĂ« * Rreth/* BashkĂ«ngjitu-nyje. Nested Loop, Hash Anti Join,⊠â diçka e tillĂ«.
Të shfrytëzojmë logjikën e thjeshtë: nëse kemi tabelat A dhe B, të cilat «bashkohen» me njëra-tjetrën në plan, atëherë ato mund të kenë qenë të vendosura në kërkesë si A-JOIN-B, ose B-JOIN-A. Le t'i kombinojmë kështu, le t'i kombinojmë për ndryshe, dhe kështu deri sa të përfundojnë këto çifte.
Të marrim pemën tonë sintaksore, të marrim planin tonë, t'i shohim ato⊠nuk duket!

Ta vizatojmĂ« atĂ« nĂ« formĂ« grafikĂ«sh â o, tashmĂ« duket diçka qĂ« i ngjan diçkaje!

Le tĂ« vĂ«mĂ« re se kemi nodet qĂ« kanĂ« njĂ«kohĂ«sisht fĂ«mijĂ« B dhe C â nuk ka rĂ«ndĂ«si nĂ« cilin rend. Do t'i bashkojmĂ« dhe do ta kthejmĂ« imazhin e nodit.

TĂ« shohim pĂ«rsĂ«ri. Tani kemi nodet me fĂ«mijĂ« A dhe çiftet (B + C) â do t'i bashkojmĂ« edhe kĂ«ta.

Shkëlqyeshëm! Del se këto dy JOIN në kërkesat me nodet e planit i kemi bashkuar me sukses.
Fatkeqësisht, kjo detyrë nuk zgjidhet gjithmonë.

Për shembull, nëse në kërkesë A JOIN B JOIN C, dhe në plan iu bashkuan në radhë nodet "ekstreme" A dhe C. Dhe në kërkesë nuk ka një operator të tillë, nuk kemi asgjë për të theksuar, nuk ka për çfarë të lidhim sugjerimin. E njëjta gjë ndodh me "presjen", kur shkruani A, B.
Por, nĂ« shumicĂ«n e rasteve, pothuajse tĂ« gjitha nodet arrijnĂ« tĂ« "zgjidhen" dhe tĂ« marrin kĂ«tĂ« profilim nĂ« tĂ« majtĂ« sipas kohĂ«s â dosido, si nĂ« Google Chrome kur analizoni kodin nĂ« JavaScript. Shihni sa kohĂ« e kishte çdo rresht dhe çdo operator "ekzekutuar".

Dhe për ta bërë më të lehtë përdorimin tuaj me të gjitha këto, ne kemi bërë ruajtje , ku mund të ruani dhe më pas të gjeni planet tuaja së bashku me kërkesat e asociuara ose të ndani një lidhje me dikë.
Nëse thjesht duhet ta çoni një kërkesë të paqartë në një formë të arsyeshme, përdorni .

Burimi: habr.com
