ΠΠΎ ΡΠΎΠ΄Ρ Π΄Π΅ΡΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ ΠΏΡΠΈΡ ΠΎΠ΄ΠΈΡΡΡ ΡΡΠ°Π»ΠΊΠΈΠ²Π°ΡΡΡΡ Ρ ΡΠΈΡΡΠ°ΡΠΈΡΠΌΠΈ, ΠΊΠΎΠ³Π΄Π° ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΠΏΠΈΡΠ΅Ρ Π·Π°ΠΏΡΠΎΡ ΠΈ Π΄ΡΠΌΠ°Π΅Ρ «Π±Π°Π·Π° ΡΠΌΠ½Π°Ρ, ΡΠ°ΠΌΠ° ΡΠΎ Π²ΡΠ΅ΠΌ ΡΠΏΡΠ°Π²ΠΈΡΡΡ!«
Π Π½Π΅ΠΊΠΎΡΠΎΡΡΡ
ΡΠ»ΡΡΠ°ΡΡ
(ΡΠ°ΡΡΠΈΡΠ½ΠΎ ΠΎΡ Π½Π΅Π·Π½Π°Π½ΠΈΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠ΅ΠΉ ΠΠ, ΡΠ°ΡΡΠΈΡΠ½ΠΎ ΠΎΡ ΠΏΡΠ΅ΠΆΠ΄Π΅Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΉ) ΡΠ°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄ ΠΏΡΠΈΠ²ΠΎΠ΄ΠΈΡ ΠΊ ΠΏΠΎΡΠ²Π»Π΅Π½ΠΈΡ Β«ΡΡΠ°Π½ΠΊΠ΅Π½ΡΡΠ΅ΠΉΠ½ΠΎΠ²Β».
Π‘Π½Π°ΡΠ°Π»Π° ΠΏΡΠΈΠ²Π΅Π΄Ρ ΠΏΡΠΈΠΌΠ΅Ρ ΡΠ°ΠΊΠΎΠ³ΠΎ Π·Π°ΠΏΡΠΎΡΠ°:
-- Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠ»ΡΡΠ΅Π²ΠΎΠΉ ΠΏΠ°ΡΡ Π½Π°Ρ
ΠΎΠ΄ΠΈΠΌ Π°ΡΡΠΎΡΠΈΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΏΠΎΠ»Π΅ΠΉ
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;
Π§ΡΠΎΠ±Ρ ΠΏΡΠ΅Π΄ΠΌΠ΅ΡΠ½ΠΎ ΠΎΡΠ΅Π½ΠΈΠ²Π°ΡΡ ΠΊΠ°ΡΠ΅ΡΡΠ²ΠΎ Π·Π°ΠΏΡΠΎΡΠ°, Π΄Π°Π²Π°ΠΉΡΠ΅ ΡΠΎΠ·Π΄Π°Π΄ΠΈΠΌ Π½Π΅ΠΊΠΈΠΉ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ»ΡΠ½ΡΠΉ Π½Π°Π±ΠΎΡ Π΄Π°Π½Π½ΡΡ :
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);
ΠΠΊΠ°Π·ΡΠ²Π°Π΅ΡΡΡ, ΡΠ°ΠΌΠΎ ΡΡΠ΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ Π·Π°Π½ΡΠ»ΠΎ ΠΌΠ΅Π½ΡΡΠ΅ ΡΠ΅ΡΠ²Π΅ΡΡΠΈ Π²ΡΠ΅Π³ΠΎ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ Π·Π°ΠΏΡΠΎΡΠ°:
Π Π°Π·Π±ΠΈΡΠ°Π΅ΠΌ ΠΏΠΎ ΠΊΠΎΡΡΠΎΡΠΊΠ°ΠΌ
ΠΡΠΈΡΡΠ°Π»ΡΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡΡΠΈΠΌ Π½Π° Π·Π°ΠΏΡΠΎΡ, ΠΈ ΠΎΠ·Π°Π΄Π°ΡΠΈΠΌΡΡ:
- ΠΠ°ΡΠ΅ΠΌ ΡΡΡ WITH RECURSIVE, Π΅ΡΠ»ΠΈ Π½ΠΈΠΊΠ°ΠΊΠΈΡ ΡΠ΅ΠΊΡΡΡΠΈΠ²Π½ΡΡ CTE β Π½Π΅ΡΡ?
- ΠΠ°ΡΠ΅ΠΌ Π³ΡΡΠΏΠΏΠΈΡΠΎΠ²Π°ΡΡ min/max-Π·Π½Π°ΡΠ΅Π½ΠΈΡ Π² ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎΠΉ CTE, Π΅ΡΠ»ΠΈ ΠΏΠΎΡΠΎΠΌ ΠΎΠ½ΠΈ Π²ΡΠ΅ ΡΠ°Π²Π½ΠΎ ΠΏΡΠΈΠ²ΡΠ·ΡΠ²Π°ΡΡΡΡ ΠΊ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»ΡΠ½ΠΎΠΉ Π²ΡΠ±ΠΎΡΠΊΠ΅?
+25% Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ - ΠΠ°ΡΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π² ΠΊΠΎΠ½ΡΠ΅ ΠΏΠΎΠ²ΡΠΎΡΠ½ΡΡ Π½Π°ΡΠΈΡΠΊΡ ΠΈΠ· ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠ΅ΠΉ CTE ΡΠ΅ΡΠ΅Π· Π±Π΅Π·ΡΡΠ»ΠΎΠ²Π½ΡΠΉ ‘SELECT * FROM’?
+14% Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ
Π Π΄Π°Π½Π½ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ Π½Π°ΠΌ Π΅ΡΠ΅ ΡΠΈΠ»ΡΠ½ΠΎ ΠΏΠΎΠ²Π΅Π·Π»ΠΎ, ΡΡΠΎ Π΄Π»Ρ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΡ Π±ΡΠ» Π²ΡΠ±ΡΠ°Π½ Hash Join, Π° Π½Π΅ Nested Loop, ΠΏΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΡΠΎΠ³Π΄Π° ΠΌΡ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΈ Π±Ρ Π½Π΅ ΠΎΠ΄ΠΈΠ½-Π΅Π΄ΠΈΠ½ΡΡΠ²Π΅Π½Π½ΡΠΉ ΠΏΡΠΎΡ ΠΎΠ΄ CTE Scan, Π° 10K!
Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΠΎ CTE ScanΠ’ΡΡ Π½Π°Π΄ΠΎ Π²ΡΠΏΠΎΠΌΠ½ΠΈΡΡ, ΡΡΠΎ CTE Scan ΡΠ²Π»ΡΠ΅ΡΡΡ Π°Π½Π°Π»ΠΎΠ³ΠΎΠΌ Seq Scan β ΡΠΎ Π΅ΡΡΡ Π½ΠΈΠΊΠ°ΠΊΠΎΠΉ ΠΈΠ½Π΄Π΅ΠΊΡΠ°ΡΠΈΠΈ, Π° ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΠ»Π½ΡΠΉ ΠΏΠ΅ΡΠ΅Π±ΠΎΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΏΠΎΡΡΠ΅Π±ΠΎΠ²Π°Π» Π±Ρ 10K x 0.3ms = 3000ms ΠΏΡΠΈ ΡΠΈΠΊΠ»Π°Ρ
ΠΏΠΎ cte_max ΠΈΠ»ΠΈ 1K x 1.5ms = 1500ms ΠΏΡΠΈ ΡΠΈΠΊΠ»Π°Ρ
ΠΏΠΎ cte_bind!
Π‘ΠΎΠ±ΡΡΠ²Π΅Π½Π½ΠΎ, Π° ΡΡΠΎ Ρ
ΠΎΡΠ΅Π»ΠΈ ΠΏΠΎΠ»ΡΡΠΈΡΡ-ΡΠΎ Π² ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ΅? ΠΠ³Π°, ΠΎΠ±ΡΡΠ½ΠΎ ΠΈΠΌΠ΅Π½Π½ΠΎ ΡΠ°ΠΊΠΎΠΉ Π²ΠΎΠΏΡΠΎΡ ΠΈ ΠΏΠΎΡΠ΅ΡΠ°Π΅Ρ Π³Π΄Π΅-ΡΠΎ Π½Π° 5ΠΉ ΠΌΠΈΠ½ΡΡΠ΅ ΡΠ°Π·Π±ΠΎΡΠ° Β«ΡΡΠ΅Ρ
ΡΡΠ°ΠΆΠ½ΡΡ
Β» Π·Π°ΠΏΡΠΎΡΠΎΠ².
ΠΡ Ρ
ΠΎΡΠ΅Π»ΠΈ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΠΎΠΉ ΠΊΠ»ΡΡΠ΅Π²ΠΎΠΉ ΠΏΠ°ΡΡ Π²ΡΠ²Π΅ΡΡΠΈ min/max ΠΈΠ· Π³ΡΡΠΏΠΏΡ ΠΏΠΎ key_a.
Π’Π°ΠΊ Π²ΠΎΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΡ ΠΆΠ΅ Π΄Π»Ρ ΡΡΠΎΠ³ΠΎ
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);
ΠΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΡΡΠ΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ Π² ΠΎΠ±ΠΎΠΈΡ Π²Π°ΡΠΈΠ°Π½ΡΠ°Ρ Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²ΠΎ ΠΏΡΠΈΠΌΠ΅ΡΠ½ΠΎ 4-5ms, ΡΠΎ Π²Π΅ΡΡ Π½Π°Ρ Π²ΡΠΈΠ³ΡΡΡ ΠΏΠΎ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ -32% β ΡΡΠΎ Π² ΡΠΈΡΡΠΎΠΌ Π²ΠΈΠ΄Π΅ Π½Π°Π³ΡΡΠ·ΠΊΠ°, ΡΠ±ΡΠ°Π½Π½Π°Ρ Ρ CPU Π±Π°Π·Ρ, Π΅ΡΠ»ΠΈ ΡΠ°ΠΊΠΎΠΉ Π·Π°ΠΏΡΠΎΡ Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΡΡΡ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΡΠ°ΡΡΠΎ.
Π ΠΎΠ±ΡΠ΅ΠΌ, Π½Π΅ ΡΡΠΎΠΈΡ Π±Π°Π·Ρ Π·Π°ΡΡΠ°Π²Π»ΡΡΡ Β«ΠΊΡΡΠ³Π»ΠΎΠ΅ β Π½ΠΎΡΠΈΡΡ, ΠΊΠ²Π°Π΄ΡΠ°ΡΠ½ΠΎΠ΅ β ΠΊΠ°ΡΠ°ΡΡΒ».
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com