IdĆnkĂ©nt felmerĂŒl a szĂŒksĂ©g, hogy összefĂŒggĆ adatokat keressĂŒnk egy kulcskĂ©szlet alapjĂĄn, amĂg el nem Ă©rjĂŒk a kĂvĂĄnt összesĂtett rekordok szĂĄmĂĄt..
A legĂ©letszerƱbb pĂ©lda â megjelenĂteni a 20 legöregebb feladatot,amelyek szerepelnek a munkatĂĄrsak listĂĄjĂĄn, (pĂ©ldĂĄul egy adott rĂ©szlegen belĂŒl). KĂŒlönbözĆ menedzsment âdashbordokâ esetĂ©ben, amelyek rövid összefoglalĂłkat adnak a munkaterĂŒletekrĆl, hasonlĂł tĂ©ma elĂ©g gyakran szĂŒksĂ©ges.

A cikkben a PostgreSQL ânaivâ megoldĂĄsĂĄnak megvalĂłsĂtĂĄsĂĄt, egy âokosabbâ Ă©s egy igazĂĄn bonyolult algoritmust fogunk megvizsgĂĄlni, SQL âciklusâ pĂ©ldĂĄjĂĄn keresztĂŒl, amely a megtalĂĄlt adatok alapjĂĄn lĂ©p ki,ami hasznos lehet a szemĂ©lyes fejlĆdĂ©s szempontjĂĄbĂłl, valamint mĂĄs hasonlĂł esetekben valĂł alkalmazĂĄsra is.
VegyĂŒnk egy tesztadat-kĂ©szletet, Ahhoz, hogy a megjelenĂtett rekordok ne âugrĂĄljanakâ alkalomrĂłl alkalomra a rendezett Ă©rtĂ©kek megegyezĂ©sekor, tĂĄgĂtsuk az indexet az elsĆdleges kulcs hozzĂĄadĂĄsĂĄval.Ez egyszerre egyedivĂ© is teszi, Ă©s garantĂĄlja szĂĄmunkra a rendezĂ©si sorrend egyĂ©rtelmƱsĂ©gĂ©t:
CREATE INDEX ON task(owner_id, task_date, id);
-- a rĂ©git töröljĂŒk
DROP INDEX task_owner_id_task_date_idx;Ahogy hallatszik, Ășgy is ĂrjĂĄk.
ElĆször is vĂĄzoljuk fel a leginkĂĄbb alapvetĆ lekĂ©rdezĂ©st, azzal, hogy az ĂŒgyfelek ID-jĂĄt :
SELECT
*
FROM
task
WHERE
owner_id = ANY('{1,2,4,8,16,32,64,128,256,512}'::integer[])
ORDER BY
task_date, id
LIMIT 20; 
Kicsit szomorĂș â csak 20 rekordot kĂ©rtĂŒnk, de az Index Scan összesen 960 sort adott vissza,amelyeket mĂ©g rendezni is kellett... Na, prĂłbĂĄljuk meg kevesebbet olvasni.
unnest + ARRAY
Az elsĆ Ă©szrevĂ©tel, ami segĂteni fog â ha csak 20 rendezett rekordra van szĂŒksĂ©gĂŒnk, akkor elegendĆ csak 20 rendezett rekordot olvasni a megfelelĆ sorrendben minden egyes kulcshoz. SzerencsĂ©re, a megfelelĆ index (owner_id, task_date, id) mĂĄr lĂ©tezik nĂĄlunk. HasznĂĄljuk ugyanazt a mechanizmust az adatkinyerĂ©shez Ă©s a âhasĂĄbokba fordĂtĂĄshozâ
az egĂ©sz tĂĄbla rekordjĂĄn , mint a. Emellett alkalmazzuk az ARRAY() funkciĂłt a tömbokkĂĄ valĂł tömörĂtĂ©shez. WITH T AS ( SELECT unnest(ARRAY( SELECT t FROM task t WHERE owner_id = unnest ORDER BY task_date, id LIMIT 20 -- itt korlĂĄtozzuk... )) r FROM unnest('{1,2,4,8,16,32,64,128,256,512}'::integer[]) ) SELECT (r).* FROM T ORDER BY (r).task_date, (r).id LIMIT 20; -- ... Ă©s itt is - ugyanĂșgy Oh, mĂĄr sokkal jobb!:
40%-kal gyorsabb, Ă©s 4,5-szer kevesebb adatot 
kellett olvasni. A tĂĄblĂĄzatok rekordjainak anyagba ĂĄllĂtĂĄsa CTE-n keresztĂŒl. FelhĂvom a figyelmet arra, hogy
a rekordok bevonĂĄsa a cĂ©lnak megfelelĆen törtĂ©nik, Ă©s a szĂŒksĂ©ges adatokkal gazdagodik,amely az eredmĂ©nyek megfelelĆ Ă©rtelmezĂ©sĂ©t garantĂĄlja. bizonyos esetekben a keresett rekord mezĆivel valĂł közvetlen munkavĂ©gzĂ©s a beĂĄgyazott lekĂ©rdezĂ©s utĂĄn, anĂ©lkĂŒl, hogy "CTE-be" csomagolnĂĄnk, ilyenekhez vezethet a InitPlan "sokszorozĂĄsĂĄhoz" az emlĂtett mezĆk szĂĄmĂĄnak megfelelĆen:
SELECT
((
SELECT
t
FROM
task t
WHERE
owner_id = 1
ORDER BY
task_date, id
LIMIT 1
).*);EredmĂ©ny (költsĂ©g=4.77..4.78 sor=1 szĂ©lessĂ©g=16) (valĂłs idĆ=0.063..0.063 sor=1 ciklus=1)
Pufferek: megosztott hit=16
InitPlan 1 (visszaadja $0)
-> Limit (költsĂ©g=0.42..1.19 sor=1 szĂ©lessĂ©g=48) (valĂłs idĆ=0.031..0.032 sor=1 ciklus=1)
Pufferek: megosztott hit=4
-> Index ĂtvizsgĂĄlĂĄs a task_owner_id_task_date_id_idx index alapjĂĄn a task t-n (költsĂ©g=0.42..387.57 sor=500 szĂ©lessĂ©g=48) (valĂłs idĆ=0.030..0.030 sor=1 ciklus=1)
Index feltétel: (owner_id = 1)
Pufferek: megosztott hit=4
InitPlan 2 (visszaadja $1)
-> Limit (költsĂ©g=0.42..1.19 sor=1 szĂ©lessĂ©g=48) (valĂłs idĆ=0.008..0.009 sor=1 ciklus=1)
Pufferek: megosztott hit=4
-> Index ĂtvizsgĂĄlĂĄs a task_owner_id_task_date_id_idx index alapjĂĄn a task t_1-en (költsĂ©g=0.42..387.57 sor=500 szĂ©lessĂ©g=48) (valĂłs idĆ=0.008..0.008 sor=1 ciklus=1)
Index feltétel: (owner_id = 1)
Pufferek: megosztott hit=4
InitPlan 3 (visszaadja $2)
-> Limit (költsĂ©g=0.42..1.19 sor=1 szĂ©lessĂ©g=48) (valĂłs idĆ=0.008..0.008 sor=1 ciklus=1)
Pufferek: megosztott hit=4
-> Index ĂtvizsgĂĄlĂĄs a task_owner_id_task_date_id_idx index alapjĂĄn a task t_2-n (költsĂ©g=0.42..387.57 sor=500 szĂ©lessĂ©g=48) (valĂłs idĆ=0.008..0.008 sor=1 ciklus=1)
Index feltétel: (owner_id = 1)
Pufferek: megosztott hit=4
InitPlan 4 (visszaadja $3)
-> Limit (költsĂ©g=0.42..1.19 sor=1 szĂ©lessĂ©g=48) (valĂłs idĆ=0.009..0.009 sor=1 ciklus=1)
Pufferek: megosztott hit=4
-> Index ĂtvizsgĂĄlĂĄs a task_owner_id_task_date_id_idx index alapjĂĄn a task t_3-on (költsĂ©g=0.42..387.57 sor=500 szĂ©lessĂ©g=48) (valĂłs idĆ=0.009..0.009 sor=1 ciklus=1)
Index feltétel: (owner_id = 1)
Pufferek: megosztott hit=4
Ugyanazt a rekordot "kerestĂŒk" meg 4 alkalommal... A PostgreSQL 11-ig ez a viselkedĂ©s rendszeres volt, Ă©s a megoldĂĄs a "CTE-be" valĂł csomagolĂĄs, amely ezekben a verziĂłkban a lekĂ©rdezĂ©soptimalizĂĄlĂł hatĂĄrvonalĂĄt kĂ©pezi.
RekurzĂv akkumulĂĄtor
A korĂĄbbi vĂĄltozatban összesen 200 sort olvastunk a szĂŒksĂ©ges 20-Ă©rt. MĂĄr nem 960, de mĂ©g kevesebb â lehetsĂ©ges?
PrĂłbĂĄljuk meg felhasznĂĄlni azt a tudĂĄst, hogy nekĂŒnk összesen 20 rekordra van szĂŒksĂ©gĂŒnk. TehĂĄt csak addig iterĂĄlunk az adatok kiolvasĂĄsĂĄn, amĂg elĂ©rjĂŒk a szĂŒksĂ©ges mennyisĂ©get.
1. lĂ©pĂ©s: kezdĆ lista
NyilvĂĄnvalĂł, hogy a cĂ©lzott 20 rekordbĂłl ĂĄllĂł listĂĄnknak az "elsĆ" rekordokkal kell kezdenie, egyes owner_id kulcsok között. EzĂ©rt elĆször ilyeneket kell talĂĄlnunk "legelsĆket" minden egyes kulcs alapjĂĄn Ă©s fel kell venni a listĂĄba, a kĂvĂĄnt sorrendben rendezve â (task_date, id).

2. lĂ©pĂ©s: megtalĂĄljuk a "következĆ" rekordokat
Most, ha az elsĆ rekordot vesszĂŒk a listĂĄnkbĂłl Ă©s elkezdĂŒnk "lĂ©pkedni" tovĂĄbb az indexen a tulajdonos_azonosĂtĂł kulcs megtartĂĄsĂĄval az összes megtalĂĄlt bejegyzĂ©s pontosan a következĆ a lekĂ©rdezĂ©si eredmĂ©nyben. TermĂ©szetesen csak amĂg nem lĂ©pjĂŒk ĂĄt az alkalmazĂĄsi kulcsot a lista mĂĄsodik bejegyzĂ©sĂ©t.
Ha Ășgy alakult, hogy a mĂĄsodik bejegyzĂ©st "ĂĄtlĂ©ptĂŒk", akkor az utolsĂł olvasott bejegyzĂ©st kell hozzĂĄadni a listĂĄhoz az elsĆ helyett (ugyanazzal a tulajdonos_azonosĂtĂłval), majd a listĂĄt Ășjra rendezni kell.

Ez azt jelenti, hogy folyamatosan olyan helyzet ĂĄll elĆ, hogy a listĂĄban legfeljebb egy bejegyzĂ©s van minden kulcs szĂĄmĂĄra (ha a bejegyzĂ©sek elfogytak, Ă©s mi nem "lĂ©ptĂŒk ĂĄt", akkor az elsĆ bejegyzĂ©s egyszerƱen eltƱnik a listĂĄbĂłl, Ă©s semmi nem kerĂŒl hozzĂĄ), Ă©s ezek mindig rendezettek az alkalmazĂĄsi kulcs (task_date, id) növekvĆ sorrendjĂ©ben.

3. lĂ©pĂ©s: szƱrjĂŒk Ă©s "kiterjesztjĂŒk" a bejegyzĂ©seket
A rekuzĂv lekĂ©rdezĂ©s egyes sorai esetĂ©ben nĂ©hĂĄny bejegyzĂ©s rv megismĂ©tlĆdik - elĆször megtalĂĄljuk azokat, amelyek "ĂĄtlĂ©pik a lista 2. bejegyzĂ©sĂ©nek hatĂĄrĂĄt", majd az elsĆkĂ©nt helyettesĂtjĂŒk Ćket a listĂĄban. Nos, az elsĆ elĆfordulĂĄsĂĄt ki kell szƱrni.
SzörnyƱ vĂ©gsĆ lekĂ©rdezĂ©s
REKURZĂV T KĂPVISELĆJELENETAS SELECT * GI SORNĂKRA MINDEN KULCSOT RĂSZĂRE KIALAKĂTOTT KEDVEZMĂNYLEHETĆSĂGKĂRĂSSEL MINDEN KULCSOT KĂLĂN KELLETT HASZNĂLNI! A WRAP ( - A HESTE VISSZAHĂZĂSĂVAL, ĂS A SEGĂTSĂGEK NEM CSĂKKENK FOLYAMATOS LEĂRĂSA MALCOLM ELEMENTĂRI RECORD-HOZ TISZTESSĂG RĂF âFELBONTĂSâ AZ HATĂANYAGOK MINT KĂLĂN ĂSSZEFONĂDĂSKI, TEHĂT A 3-IK HATĂANYAGOT KIZĂRJA, RĂG KEDVEZMĂNY ĂS MULTIPLICEI FORGĂK KERĂLNEK A LISTĂBĂL. LEGOLDALASBAN A FELIRAT NEM LETTED EZT A PONTOT, HANEM ENNEK AZ OLDTALAN PONTNAK A FĆ LEBONYOLĂTĂSĂVAL RĂGIVEL, HANEM KIEGĂSZĂTETT A KONDĂCIĂVAL KAPCSOLĂDĂ TARTALMAZĂSNAK FEJLĆDĂSĂRE. 
EzĂ©rt 50%-os adatolvasĂĄsra 20%-os futĂĄsi idĆt cserĂ©ltĂŒnk.TehĂĄt, ha Ășgy gondolja, hogy az olvasĂĄs hosszĂș ideig eltarthat (pĂ©ldĂĄul, ha az adatok gyakran nincsenek a gyorsĂtĂłtĂĄrban, Ă©s a lemezen kell Ćket keresni), akkor ebben az esetben kevesebbet lehet fĂŒggni az olvasĂĄstĂłl.
Mindenesetre a vĂ©grehajtĂĄsi idĆ jobban alakult, mint a ânaivâ elsĆ vĂĄltozatban. De hogy melyik ezek közĂŒl a 3 vĂĄltozatot hasznĂĄlja â az az Ăn döntĂ©se.
ForrĂĄs: habr.com
