Ngamaxesha athile, umsebenzi wokukhangela idatha ehambelanayo usebenzisa isethi yezitshixo ivela. de sifumane inani elifunekayo lilonke leerekhodi.
Owona mzekelo βwobomi beneneβ kukubonisa 20 iingxaki ezindala, zidweliswe kuluhlu lwabasebenzi (umzekelo, phakathi kwecandelo elinye). Kulawulo olwahlukeneyo βlweedeshibhodiβ ezinezishwankathelo ezimfutshane zeendawo zokusebenza, isihloko esifanayo siyafuneka rhoqo.
Kweli nqaku siza kujonga ukuphunyezwa kwe-PostgreSQL yesisombululo "sokungenangqondo" kwingxaki enjalo, "i-smarter" kunye ne-algorithm enzima kakhulu. "iluphu" kwiSQL enemeko yokuphuma kwidatha efunyenweyo, enokuba luncedo kokubini kuphuhliso jikelele kunye nokusetyenziswa kwezinye iimeko ezifanayo.
Masithathe isethi yedatha yovavanyo ukusuka
CREATE INDEX ON task(owner_id, task_date, id);
-- Π° ΡΡΠ°ΡΡΠΉ - ΡΠ΄Π°Π»ΠΈΠΌ
DROP INDEX task_owner_id_task_date_idx;
njengoko liviwe, ngokunjalo kubhaliwe kwathiwa;
Okokuqala, makhe sizobe olona guqulelo lulula lwesicelo, sigqithise ii-ID zabadlali
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;
Okubuhlungu kancinci - si-odole iirekhodi ezingama-20 kuphela, kodwa i-Index Scan isibuyisele kuthi 960 imigca, leyo ke nayo kwakufuneka ihlelwe... Masizame ukufunda kancinci.
unnest + ARRAY
Ingqwalaselo yokuqala eya kusinceda kuxa sifuna ngama-20 kuphela ahleliweyo iirekhodi, uze ufunde nje akukho ngaphezu kwama-20 ahlelwe ngokulandelelana okufanayo kwinto nganye isitshixo. Kulungile, isalathisi esifanelekileyo (owner_id, task_date, id) sinayo.
Masisebenzise indlela efanayo yokukhupha kunye "nokusasaza kwiikholamu" irekhodi yetheyibhile edibeneyo, njengoba ARRAY()
:
WITH T AS (
SELECT
unnest(ARRAY(
SELECT
t
FROM
task t
WHERE
owner_id = unnest
ORDER BY
task_date, id
LIMIT 20 -- ΠΎΠ³ΡΠ°Π½ΠΈΡΠΈΠ²Π°Π΅ΠΌ ΡΡΡ...
)) 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; -- ... ΠΈ ΡΡΡ - ΡΠΎΠΆΠ΅
Owu, ngcono kakhulu! I-40% ngokukhawuleza kunye namaxesha e-4.5 ngaphantsi kwedatha Kwafuneka ndiyifunde.
Ukusetyenziswa kweerekhodi zetafile nge-CTEManditsalele ingqalelo yakho kwinto yokuba kwezinye iimeko Inzame yokusebenza ngokukhawuleza kunye nemimandla yerekhodi emva kokuyikhangela kwi-subquery, ngaphandle "kokuyisonga" kwi-CTE, kunokukhokelela ekubeni "phinda-phinda" InitPlan ngokomlinganiselo wenani lale mihlaba inye:
SELECT
((
SELECT
t
FROM
task t
WHERE
owner_id = 1
ORDER BY
task_date, id
LIMIT 1
).*);
Result (cost=4.77..4.78 rows=1 width=16) (actual time=0.063..0.063 rows=1 loops=1)
Buffers: shared hit=16
InitPlan 1 (returns $0)
-> Limit (cost=0.42..1.19 rows=1 width=48) (actual time=0.031..0.032 rows=1 loops=1)
Buffers: shared hit=4
-> Index Scan using task_owner_id_task_date_id_idx on task t (cost=0.42..387.57 rows=500 width=48) (actual time=0.030..0.030 rows=1 loops=1)
Index Cond: (owner_id = 1)
Buffers: shared hit=4
InitPlan 2 (returns $1)
-> Limit (cost=0.42..1.19 rows=1 width=48) (actual time=0.008..0.009 rows=1 loops=1)
Buffers: shared hit=4
-> Index Scan using task_owner_id_task_date_id_idx on task t_1 (cost=0.42..387.57 rows=500 width=48) (actual time=0.008..0.008 rows=1 loops=1)
Index Cond: (owner_id = 1)
Buffers: shared hit=4
InitPlan 3 (returns $2)
-> Limit (cost=0.42..1.19 rows=1 width=48) (actual time=0.008..0.008 rows=1 loops=1)
Buffers: shared hit=4
-> Index Scan using task_owner_id_task_date_id_idx on task t_2 (cost=0.42..387.57 rows=500 width=48) (actual time=0.008..0.008 rows=1 loops=1)
Index Cond: (owner_id = 1)
Buffers: shared hit=4"
InitPlan 4 (returns $3)
-> Limit (cost=0.42..1.19 rows=1 width=48) (actual time=0.009..0.009 rows=1 loops=1)
Buffers: shared hit=4
-> Index Scan using task_owner_id_task_date_id_idx on task t_3 (cost=0.42..387.57 rows=500 width=48) (actual time=0.009..0.009 rows=1 loops=1)
Index Cond: (owner_id = 1)
Buffers: shared hit=4
Irekhodi efanayo "yajongwa phezulu" ngamaxesha e-4 ... Kuze kube yi-PostgreSQL 11, oku kuziphatha kwenzeka rhoqo, kwaye isisombululo "kukuyisonga" kwi-CTE, engumda opheleleyo we-optimizer kwezi nguqulelo.
I-recursive accumulator
Kwinguqulelo yangaphambili, xa sisonke sifunda 200 imigca ngenxa efunekayo 20. Hayi 960, kodwa nangaphantsi - ngaba kunokwenzeka?
Masizame ukusebenzisa ulwazi esiludingayo 20 iyonke iirekhodi. Oko kukuthi, siya kuphindaphinda ukufundwa kwedatha kuphela de sifikelele kwisixa esisidingayo.
Inyathelo 1: Uluhlu lokuqalisa
Ngokucacileyo, uluhlu lwethu "olujoliswe kuko" lweerekhodi ezingama-20 kufuneka luqale ngeerekhodi "zokuqala" kwesinye sezitshixo zethu ze-ident_id. Ngoko ke, okokuqala siya kufumana ezinjalo βkuqala kakhuluβ kwisitshixo ngasinye kwaye uyongeze kuluhlu, ukuyibeka ngendlela esiyifunayo - (umhla womsebenzi, id).
Inyathelo lesi-2: Fumana ingeniso "elandelayo".
Ngoku ukuba sithatha ingeniso yokuqala kuluhlu lwethu kwaye siqale βnyatheloβ ngakumbi ecaleni kwesalathiso Ukugcina umnikazi_id isitshixo, emva koko zonke iirekhodi ezifunyenweyo zizona zilandelayo kukhetho olunesiphumo. Ewe, kuphela side siwele isitshixo sempundu ungeno lwesibini kuluhlu.
Ukuba kuvela ukuba "siwele" irekhodi yesibini, ngoko ungeno lokugqibela olufundiweyo kufuneka longezwe kuluhlu endaweni yelokuqala (nge owner_id efanayo), emva koko siphinde sihlele uluhlu kwakhona.
Oko kukuthi, sihlala sifumana ukuba uluhlu alunangeniso engaphezulu kwesinye kwisitshixo ngasinye (ukuba amangeno ayaphela kwaye "asiweleli", ngoko ke ungeniso lokuqala oluvela kuluhlu luya kunyamalala kwaye akukho nto iya kongezwa. ), kunye nabo yahlala yahlelwa ngokonyuka kolandelelwano lweqhosha lesicelo (task_date, id).
Inyathelo 3: icebo lokucoca kunye "nokwandisa" iirekhodi
Kweminye yemigca yokhetho lwethu oluphindaphindiweyo, ezinye iirekhodi rv
ziphindaphindwe - okokuqala sifumana "njengokuwela umda wokungena kwe-2 kuluhlu", kwaye ke uyifake endaweni yoku-1 kuluhlu. Ngoko isehlo sokuqala kufuneka sihluzwe.
Umbuzo wokugqibela owoyikayo
WITH RECURSIVE T AS (
-- #1 : Π·Π°Π½ΠΎΡΠΈΠΌ Π² ΡΠΏΠΈΡΠΎΠΊ "ΠΏΠ΅ΡΠ²ΡΠ΅" Π·Π°ΠΏΠΈΡΠΈ ΠΏΠΎ ΠΊΠ°ΠΆΠ΄ΠΎΠΌΡ ΠΈΠ· ΠΊΠ»ΡΡΠ΅ΠΉ Π½Π°Π±ΠΎΡΠ°
WITH wrap AS ( -- "ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΡΠ΅ΠΌ" record'Ρ, ΡΡΠΎΠ±Ρ ΠΎΠ±ΡΠ°ΡΠ΅Π½ΠΈΠ΅ ΠΊ ΠΏΠΎΠ»ΡΠΌ Π½Π΅ Π²ΡΠ·ΡΠ²Π°Π»ΠΎ ΡΠΌΠ½ΠΎΠΆΠ΅Π½ΠΈΡ InitPlan/SubPlan
WITH T AS (
SELECT
(
SELECT
r
FROM
task r
WHERE
owner_id = unnest
ORDER BY
task_date, id
LIMIT 1
) r
FROM
unnest('{1,2,4,8,16,32,64,128,256,512}'::integer[])
)
SELECT
array_agg(r ORDER BY (r).task_date, (r).id) list -- ΡΠΎΡΡΠΈΡΡΠ΅ΠΌ ΡΠΏΠΈΡΠΎΠΊ Π² Π½ΡΠΆΠ½ΠΎΠΌ ΠΏΠΎΡΡΠ΄ΠΊΠ΅
FROM
T
)
SELECT
list
, list[1] rv
, FALSE not_cross
, 0 size
FROM
wrap
UNION ALL
-- #2 : Π²ΡΡΠΈΡΡΠ²Π°Π΅ΠΌ Π·Π°ΠΏΠΈΡΠΈ 1-Π³ΠΎ ΠΏΠΎ ΠΏΠΎΡΡΠ΄ΠΊΡ ΠΊΠ»ΡΡΠ°, ΠΏΠΎΠΊΠ° Π½Π΅ ΠΏΠ΅ΡΠ΅ΡΠ°Π³Π½Π΅ΠΌ ΡΠ΅ΡΠ΅Π· Π·Π°ΠΏΠΈΡΡ 2-Π³ΠΎ
SELECT
CASE
-- Π΅ΡΠ»ΠΈ Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ Π΄Π»Ρ ΠΊΠ»ΡΡΠ° 1-ΠΉ Π·Π°ΠΏΠΈΡΠΈ
WHEN X._r IS NOT DISTINCT FROM NULL THEN
T.list[2:] -- ΡΠ±ΠΈΡΠ°Π΅ΠΌ Π΅Π΅ ΠΈΠ· ΡΠΏΠΈΡΠΊΠ°
-- Π΅ΡΠ»ΠΈ ΠΌΡ ΠΠ ΠΏΠ΅ΡΠ΅ΡΠ΅ΠΊΠ»ΠΈ ΠΏΡΠΈΠΊΠ»Π°Π΄Π½ΠΎΠΉ ΠΊΠ»ΡΡ 2-ΠΉ Π·Π°ΠΏΠΈΡΠΈ
WHEN X.not_cross THEN
T.list -- ΠΏΡΠΎΡΡΠΎ ΠΏΡΠΎΡΡΠ³ΠΈΠ²Π°Π΅ΠΌ ΡΠΎΡ ΠΆΠ΅ ΡΠΏΠΈΡΠΎΠΊ Π±Π΅Π· ΠΌΠΎΠ΄ΠΈΡΠΈΠΊΠ°ΡΠΈΠΉ
-- Π΅ΡΠ»ΠΈ Π² ΡΠΏΠΈΡΠΊΠ΅ ΡΠΆΠ΅ Π½Π΅Ρ 2-ΠΉ Π·Π°ΠΏΠΈΡΠΈ
WHEN T.list[2] IS NULL THEN
-- ΠΏΡΠΎΡΡΠΎ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌ ΠΏΡΡΡΠΎΠΉ ΡΠΏΠΈΡΠΎΠΊ
'{}'
-- ΠΏΠ΅ΡΠ΅ΡΠΎΡΡΠΈΡΠΎΠ²ΡΠ²Π°Π΅ΠΌ ΡΠ»ΠΎΠ²Π°ΡΡ, ΡΠ±ΠΈΡΠ°Ρ 1-Ρ Π·Π°ΠΏΠΈΡΡ ΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡ ΠΏΠΎΡΠ»Π΅Π΄Π½ΡΡ ΠΈΠ· Π½Π°ΠΉΠ΄Π΅Π½Π½ΡΡ
ELSE (
SELECT
coalesce(T.list[2] || array_agg(r ORDER BY (r).task_date, (r).id), '{}')
FROM
unnest(T.list[3:] || X._r) r
)
END
, X._r
, X.not_cross
, T.size + X.not_cross::integer
FROM
T
, LATERAL(
WITH wrap AS ( -- "ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΡΠ΅ΠΌ" record
SELECT
CASE
-- Π΅ΡΠ»ΠΈ Π²ΡΠ΅-ΡΠ°ΠΊΠΈ "ΠΏΠ΅ΡΠ΅ΡΠ°Π³Π½ΡΠ»ΠΈ" ΡΠ΅ΡΠ΅Π· 2-Ρ Π·Π°ΠΏΠΈΡΡ
WHEN NOT T.not_cross
-- ΡΠΎ Π½ΡΠΆΠ½Π°Ρ Π·Π°ΠΏΠΈΡΡ - ΠΏΠ΅ΡΠ²Π°Ρ ΠΈΠ· ΡΠΏΠΏΠΈΡΠΊΠ°
THEN T.list[1]
ELSE ( -- Π΅ΡΠ»ΠΈ Π½Π΅ ΠΏΠ΅ΡΠ΅ΡΠ΅ΠΊΠ»ΠΈ, ΡΠΎ ΠΊΠ»ΡΡ ΠΎΡΡΠ°Π»ΡΡ ΠΊΠ°ΠΊ Π² ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠ΅ΠΉ Π·Π°ΠΏΠΈΡΠΈ - ΠΎΡΡΠ°Π»ΠΊΠΈΠ²Π°Π΅ΠΌΡΡ ΠΎΡ Π½Π΅Π΅
SELECT
_r
FROM
task _r
WHERE
owner_id = (rv).owner_id AND
(task_date, id) > ((rv).task_date, (rv).id)
ORDER BY
task_date, id
LIMIT 1
)
END _r
)
SELECT
_r
, CASE
-- Π΅ΡΠ»ΠΈ 2-ΠΉ Π·Π°ΠΏΠΈΡΠΈ ΡΠΆΠ΅ Π½Π΅Ρ Π² ΡΠΏΠΈΡΠΊΠ΅, Π½ΠΎ ΠΌΡ Ρ
ΠΎΡΡ ΡΡΠΎ-ΡΠΎ Π½Π°ΡΠ»ΠΈ
WHEN list[2] IS NULL AND _r IS DISTINCT FROM NULL THEN
TRUE
ELSE -- Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅ Π½Π°ΡΠ»ΠΈ ΠΈΠ»ΠΈ "ΠΏΠ΅ΡΠ΅ΡΠ°Π³Π½ΡΠ»ΠΈ"
coalesce(((_r).task_date, (_r).id) < ((list[2]).task_date, (list[2]).id), FALSE)
END not_cross
FROM
wrap
) X
WHERE
T.size < 20 AND -- ΠΎΠ³ΡΠ°Π½ΠΈΡΠΈΠ²Π°Π΅ΠΌ ΡΡΡ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ
T.list IS DISTINCT FROM '{}' -- ΠΈΠ»ΠΈ ΠΏΠΎΠΊΠ° ΡΠΏΠΈΡΠΎΠΊ Π½Π΅ ΠΊΠΎΠ½ΡΠΈΠ»ΡΡ
)
-- #3 : "ΡΠ°Π·Π²ΠΎΡΠ°ΡΠΈΠ²Π°Π΅ΠΌ" Π·Π°ΠΏΠΈΡΠΈ - ΠΏΠΎΡΡΠ΄ΠΎΠΊ Π³Π°ΡΠ°Π½ΡΠΈΡΠΎΠ²Π°Π½ ΠΏΠΎ ΠΏΠΎΡΡΡΠΎΠ΅Π½ΠΈΡ
SELECT
(rv).*
FROM
T
WHERE
not_cross; -- Π±Π΅ΡΠ΅ΠΌ ΡΠΎΠ»ΡΠΊΠΎ "Π½Π΅ΠΏΠ΅ΡΠ΅ΡΠ΅ΠΊΠ°ΡΡΠΈΠ΅" Π·Π°ΠΏΠΈΡΠΈ
Ngoko ke, thina kuthengiswa i-50% yedatha efundwa kwi-20% yexesha lokubulawa. Oko kukuthi, ukuba unezizathu zokukholelwa ukuba ukufunda kunokuthatha ixesha elide (umzekelo, idatha ihlala ingekho kwi-cache, kwaye kufuneka uye kwidisk kuyo), ngoko ngale ndlela unokuxhomekeka kancinci ekufundeni. .
Kwimeko nayiphi na into, ixesha lokuphumeza liye laba ngcono kunendlela yokuqala "engenangqondo". Kodwa yeyiphi kwezi 3 iinketho zokusebenzisa ixhomekeke kuwe.
umthombo: www.habr.com