ΠΠΎΡΠ°Π΄ΠΈ ΠΌΠΎΡΡΠ° ΠΏΡΠΎΡΠ΅ΡΠΈΡ ΡΡΡΠ±Π²Π° Π΄Π° ΡΠ΅ ΡΠΏΡΠ°Π²ΡΠΌ ΡΡΡ ΡΠΈΡΡΠ°ΡΠΈΠΈ, ΠΊΠΎΠ³Π°ΡΠΎ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ Π½Π°ΠΏΠΈΡΠ΅ Π·Π°ΡΠ²ΠΊΠ° ΠΈ ΡΠΈ ΠΏΠΎΠΌΠΈΡΠ»ΠΈ βΠΠ°Π·Π°ΡΠ° Π΅ ΡΠΌΠ½Π°, ΡΠ°ΠΌΠ° ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΡΠΏΡΠ°Π²ΠΈ Ρ Π²ΡΠΈΡΠΊΠΎ!Β«
Π Π½ΡΠΊΠΎΠΈ ΡΠ»ΡΡΠ°ΠΈ (ΠΎΡΡΠ°ΡΡΠΈ ΠΎΡ Π½Π΅ΠΏΠΎΠ·Π½Π°Π²Π°Π½Π΅ Π½Π° Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈΡΠ΅ Π½Π° Π±Π°Π·Π°ΡΠ° Π΄Π°Π½Π½ΠΈ, ΠΎΡΡΠ°ΡΡΠΈ ΠΎΡ ΠΏΡΠ΅ΠΆΠ΄Π΅Π²ΡΠ΅ΠΌΠ΅Π½Π½ΠΈ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ) ΡΠΎΠ·ΠΈ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄ Π²ΠΎΠ΄ΠΈ Π΄ΠΎ ΠΏΠΎΡΠ²Π°ΡΠ° Π½Π° βΠ€ΡΠ°Π½ΠΊΠ΅Π½ΡΠ°ΠΉΠ½ΠΈβ.
ΠΡΡΠ²ΠΎ, ΡΠ΅ Π΄Π°ΠΌ ΠΏΡΠΈΠΌΠ΅Ρ Π·Π° ΡΠ°ΠΊΠΎΠ²Π° ΠΈΡΠΊΠ°Π½Π΅:
-- Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠ»ΡΡΠ΅Π²ΠΎΠΉ ΠΏΠ°ΡΡ Π½Π°Ρ
ΠΎΠ΄ΠΈΠΌ Π°ΡΡΠΎΡΠΈΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΏΠΎΠ»Π΅ΠΉ
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?
- ΠΠ°ΡΠΎ Π΄Π° Π³ΡΡΠΏΠΈΡΠ°ΡΠ΅ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΈ/ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»Π½ΠΈ ΡΡΠΎΠΉΠ½ΠΎΡΡΠΈ Π² ΠΎΡΠ΄Π΅Π»Π΅Π½ CTE, Π°ΠΊΠΎ ΡΠ΅ ΡΠ°ΠΊΠ° ΠΈΠ»ΠΈ ΠΈΠ½Π°ΡΠ΅ ΡΠ° ΡΠ²ΡΡΠ·Π°Π½ΠΈ Ρ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»Π½Π°ΡΠ° ΠΏΡΠΎΠ±Π°?
+25% Π²ΡΠ΅ΠΌΠ΅ - ΠΠ°ΡΠΎ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΡΠ΅ Π±Π΅Π·ΡΡΠ»ΠΎΠ²Π½ΠΎ 'SELECT * FROM' Π² ΠΊΡΠ°Ρ, Π·Π° Π΄Π° ΠΏΠΎΠ²ΡΠΎΡΠΈΡΠ΅ ΠΏΡΠ΅Π΄ΠΈΡΠ½ΠΈΡ CTE?
+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% - ΡΠΎΠ²Π° Π΅ Π² Π½Π°ΠΉ-ΡΠΈΡΡ Π²ΠΈΠ΄ Π½Π°ΡΠΎΠ²Π°ΡΠ²Π°Π½Π΅ΡΠΎ Π΅ ΠΏΡΠ΅ΠΌΠ°Ρ Π½Π°ΡΠΎ ΠΎΡ ΠΎΡΠ½ΠΎΠ²Π½ΠΈΡ ΠΏΡΠΎΡΠ΅ΡΠΎΡ, Π°ΠΊΠΎ ΡΠ°ΠΊΠ°Π²Π° Π·Π°ΡΠ²ΠΊΠ° ΡΠ΅ ΠΈΠ·ΠΏΡΠ»Π½ΡΠ²Π° Π΄ΠΎΡΡΠ°ΡΡΡΠ½ΠΎ ΡΠ΅ΡΡΠΎ.
ΠΠ°ΡΠΎ ΡΡΠ»ΠΎ Π½Π΅ ΡΡΡΠ±Π²Π° Π΄Π° ΠΏΡΠΈΠ½ΡΠΆΠ΄Π°Π²Π°ΡΠ΅ ΠΎΡΠ½ΠΎΠ²Π°ΡΠ° Π΄Π° βΠ½ΠΎΡΠΈ ΠΊΡΡΠ³Π»Π°ΡΠ°, ΡΡΡΠΊΠ°Π»Ρ ΠΊΠ²Π°Π΄ΡΠ°ΡΠ½Π°ΡΠ°β.
ΠΠ·ΡΠΎΡΠ½ΠΈΠΊ: www.habr.com