Në përputhje me veprimtarinë, shpesh përballemi me situata ku zhvilluesi shkruan një kërkesë dhe mendon: "database është e mençur, do të përballet me gjithçka vetë!"«
Në disa raste (pjesërisht për shkak të mungesës së njohurive për mundësitë e BD-së, pjesërisht për shkak të optimizimeve të parakohshme) ky qasje çon në krijimin e "frankensteinëve."
Së pari do të jap një shembull të tillë të një kërkese:
-- për çdo çift çelësi gjejmë vlerat e lidhura të fushave
WITH RECURSIVE cte_bind AS (
SELECT DISTINCT ON (key_a, key_b)
key_a a
, key_b b
, fld1 bind_fld1
, fld2 bind_fld2
FROM
tbl
)
-- gjejmë min/max vlerat për çdo çelës të parë
, cte_max AS (
SELECT
a
, max(bind_fld1) bind_fld1
, min(bind_fld2) bind_fld2
FROM
cte_bind
GROUP BY
a
)
-- lidhim sipas çelësit të parë çiftet e çelësit dhe vlerat min/max
, cte_a_bind AS (
SELECT
cte_bind.a
, cte_bind.b
, cte_max.bind_fld1
, cte_max.bind_fld2
FROM
cte_bind
INNER JOIN
cte_max
ON cte_max.a = cte_bind.a
)
SELECT * FROM cte_a_bind;Për të vlerësuar cilësinë e kërkesës, le të krijojmë një grup të rastësishëm të dhënash:
CREATE TABLE tbl AS
SELECT
(random() * 1000)::integer key_a
, (random() * 1000)::integer key_b
, (random() * 10000)::integer fld1
, (random() * 10000)::integer fld2
FROM
generate_series(1, 10000);
CREATE INDEX ON tbl(key_a, key_b);
Duket se vetë leximi i të dhënave zuri më pak se një e katërta e gjithë kohës së ekzekutimit të kërkesës:

Analizojmë në detaje
Do ta shqyrtojmë kërkesën me vëmendje dhe do të shqetësohemi:
- Pse është këtu WITH RECURSIVE, nëse nuk ka CTE të përsëritura?
- Pse të grupohen vlerat min/max në një CTE të veçantë, kur ato përsëri i nënshtrohen grumbullit origjinal?
+25% kohĂ« - Pse pĂ«rdoret nĂ« fund njĂ« rikthim i pĂ«rsĂ«ritur nga CTE e mĂ«parshme pĂ«rmes âSELECT * FROMâ?
+14% kohë
Në këtë rast, na ka ecur mirë që u zgjodh Hash Join për bashkimin, jo Nested Loop, pasi përndryshe do të kishim jo një kalim të vetëm CTE Scan, por 10K!
pak pĂ«r CTE ScanKĂ«tu duhet tĂ« kujtojmĂ« se CTE Scan Ă«shtĂ« ekuivalenti i Seq Scan â pra, pa indeksim, vetĂ«m pĂ«rmes njĂ« pĂ«rzgjedhjeje tĂ« plotĂ«, qĂ« do tĂ« kĂ«rkonte 10K x 0.3ms = 3000ms nĂ« ciklet pĂ«r cte_max ose 1K x 1.5ms = 1500ms nĂ« ciklet pĂ«r cte_bind!
Në fakt, çfarë dëshironim të merrnim në rezultat? Ah, zakonisht ky është pikërisht pyetja që na vjen në mendje pas rreth 5 minutash analizimi të kërkesave "tre-katëshe."
Deshëm që për çdo çift të veçantë çelësi të nxjerrim min/max nga grupi sipas key_a.
Pra le ta përdorim këtë :
SELECT DISTINCT ON(key_a, key_b)
key_a a
, key_b b
, max(fld1) OVER(w) bind_fld1
, min(fld2) OVER(w) bind_fld2
FROM
tbl
WINDOW
w AS (PARTITION BY key_a); 
MeqenĂ«se leximi i tĂ« dhĂ«nave nĂ« tĂ« dyja rastet zĂ« afĂ«rsisht 4-5ms, gjithĂ« pĂ«rfitimi ynĂ« nĂ« kohĂ« -32% â Ă«shtĂ« thjesht ngarkesa e hequr nga CPU e bazĂ«s, nĂ«se njĂ« kĂ«rkesĂ« e tillĂ« ekzekutohet mjaft shpesh.
Në përfundim, nuk duhet ta detyrojmë bazën të "mbajë rreth" ose "rrotull ethe."
Burimi: habr.com
