Kwa sababu ya safu yangu ya kazi, lazima nishughulike na hali wakati msanidi programu anaandika ombi na kufikiria "Msingi ni wa busara, unaweza kushughulikia kila kitu yenyewe!Β«
Katika baadhi ya matukio (sehemu kutoka kwa ujinga wa uwezo wa hifadhidata, kwa sehemu kutoka kwa uboreshaji wa mapema), njia hii inaongoza kwa kuonekana kwa "Frankensteins".
Kwanza, nitatoa mfano wa ombi kama hilo:
-- Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠ»ΡΡΠ΅Π²ΠΎΠΉ ΠΏΠ°ΡΡ Π½Π°Ρ
ΠΎΠ΄ΠΈΠΌ Π°ΡΡΠΎΡΠΈΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΏΠΎΠ»Π΅ΠΉ
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
)
-- Π½Π°Ρ
ΠΎΠ΄ΠΈΠΌ min/max Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ ΠΊΠ»ΡΡΠ°
, cte_max AS (
SELECT
a
, max(bind_fld1) bind_fld1
, min(bind_fld2) bind_fld2
FROM
cte_bind
GROUP BY
a
)
-- ΡΠ²ΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΏΠΎ ΠΏΠ΅ΡΠ²ΠΎΠΌΡ ΠΊΠ»ΡΡΡ ΠΊΠ»ΡΡΠ΅Π²ΡΠ΅ ΠΏΠ°ΡΡ ΠΈ 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;
Ili kutathmini kwa kiasi kikubwa ubora wa ombi, hebu tuunde seti fulani ya data kiholela:
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);
Inageuka kuwa kusoma data kulichukua chini ya robo ya wakati Utekelezaji wa hoja:
Kuitenganisha kipande kwa kipande
Wacha tuangalie kwa karibu ombi hilo na tufadhaike:
- Kwa nini ni pamoja na KURUDISHA hapa ikiwa hakuna CTE zinazojirudia?
- Kwa nini weka thamani za min/max katika CTE tofauti ikiwa zimefungwa kwa sampuli asili hata hivyo?
+ 25% wakati - Kwa nini utumie 'CHAGUA * KUTOKA' isiyo na masharti mwishoni mwa CTE iliyotangulia?
+ 14% wakati
Katika kesi hii, tulikuwa na bahati sana kwamba Hash Join ilichaguliwa kwa muunganisho, na sio Nested Loop, kwa sababu basi hatungepokea pasi moja tu ya CTE Scan, lakini 10K!
kidogo kuhusu CTE ScanHapa lazima tukumbuke hilo CTE Scan ni sawa na Seq Scan - yaani, hakuna indexing, lakini tu utafutaji kamili, ambayo itahitaji 10K x 0.3ms = 3000ms kwa mizunguko na cte_max au 1K x 1.5ms = 1500ms wakati wa kuzunguka kwa cte_bind!
Kwa kweli, ulitaka kupata nini kama matokeo? Ndio, kwa kawaida hili ndilo swali linalokuja mahali fulani katika dakika ya 5 ya kuchambua maswali ya "hadithi tatu".
Tulitaka kutoa kwa kila jozi ya ufunguo wa kipekee min/max kutoka kwa kikundi kwa key_a.
Basi hebu kuitumia kwa hili
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);
Kwa kuwa kusoma data katika chaguzi zote mbili huchukua takriban 4-5ms sawa, basi faida yetu yote ya wakati -32% - hii ni katika fomu yake safi mzigo umeondolewa kutoka kwa msingi wa CPU, ikiwa ombi kama hilo linatekelezwa mara nyingi vya kutosha.
Kwa ujumla, haupaswi kulazimisha msingi "kubeba pande zote, tembeza mraba."
Chanzo: mapenzi.com