АнтимодСли Π½Π° PostgreSQL: CTE x CTE

ΠŸΠΎΡ€Π°Π΄ΠΈ моята профСсия трябва Π΄Π° сС справям със ситуации, ΠΊΠΎΠ³Π°Ρ‚ΠΎ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ напишС заявка ΠΈ си помисли β€žΠ‘Π°Π·Π°Ρ‚Π° Π΅ ΡƒΠΌΠ½Π°, сама ΠΌΠΎΠΆΠ΅ Π΄Π° сС справи с всичко!Β«

Π’ някои случаи (отчасти ΠΎΡ‚ Π½Π΅ΠΏΠΎΠ·Π½Π°Π²Π°Π½Π΅ Π½Π° Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΠΈΡ‚Π΅ Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ, отчасти ΠΎΡ‚ ΠΏΡ€Π΅ΠΆΠ΄Π΅Π²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ) Ρ‚ΠΎΠ·ΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ Π²ΠΎΠ΄ΠΈ Π΄ΠΎ появата Π½Π° β€žΠ€Ρ€Π°Π½ΠΊΠ΅Π½Ρ‰Π°ΠΉΠ½ΠΈβ€œ.

ΠŸΡŠΡ€Π²ΠΎ, Ρ‰Π΅ Π΄Π°ΠΌ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π·Π° Ρ‚Π°ΠΊΠΎΠ²Π° исканС:

-- для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠ»ΡŽΡ‡Π΅Π²ΠΎΠΉ ΠΏΠ°Ρ€Ρ‹ Π½Π°Ρ…ΠΎΠ΄ΠΈΠΌ ассоциированныС значСния ΠΏΠΎΠ»Π΅ΠΉ
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);

Оказва сС, Ρ‡Π΅ Ρ‡Π΅Ρ‚Π΅Π½Π΅Ρ‚ΠΎ Π½Π° Π΄Π°Π½Π½ΠΈΡ‚Π΅ ΠΎΡ‚Π½Π΅ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ ΠΎΡ‚ Π΅Π΄Π½Π° Ρ‡Π΅Ρ‚Π²ΡŠΡ€Ρ‚ ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ изпълнСниС Π½Π° заявка:

АнтимодСли Π½Π° PostgreSQL: CTE x CTE[Π²ΠΈΠΆΡ‚Π΅ expand.tensor.ru]

Разглобявайки Π³ΠΎ ΠΏΠ°Ρ€Ρ‡Π΅ ΠΏΠΎ ΠΏΠ°Ρ€Ρ‡Π΅

НСка Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΠΏΠΎ-ΠΎΡ‚Π±Π»ΠΈΠ·ΠΎ исканСто ΠΈ Π΄Π° бъдСм ΠΎΠ·Π°Π΄Π°Ρ‡Π΅Π½ΠΈ:

  1. Π—Π°Ρ‰ΠΎ Ρ‚ΡƒΠΊ Π΅ WITH RECURSIVE, Π°ΠΊΠΎ няма рСкурсивни CTE?
  2. Π—Π°Ρ‰ΠΎ Π΄Π° Π³Ρ€ΡƒΠΏΠΈΡ€Π°Ρ‚Π΅ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΈ/максимални стойности Π² ΠΎΡ‚Π΄Π΅Π»Π΅Π½ CTE, Π°ΠΊΠΎ Ρ‚Π΅ Ρ‚Π°ΠΊΠ° ΠΈΠ»ΠΈ ΠΈΠ½Π°Ρ‡Π΅ са ΡΠ²ΡŠΡ€Π·Π°Π½ΠΈ с ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»Π½Π°Ρ‚Π° ΠΏΡ€ΠΎΠ±Π°?
    +25% Π²Ρ€Π΅ΠΌΠ΅
  3. Π—Π°Ρ‰ΠΎ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ бСзусловно '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);

АнтимодСли Π½Π° PostgreSQL: CTE x CTE
[Π²ΠΈΠΆΡ‚Π΅ expand.tensor.ru]

Въй ΠΊΠ°Ρ‚ΠΎ Ρ‡Π΅Ρ‚Π΅Π½Π΅Ρ‚ΠΎ Π½Π° Π΄Π°Π½Π½ΠΈ ΠΈ Π² Π΄Π²Π΅Ρ‚Π΅ ΠΎΠΏΡ†ΠΈΠΈ ΠΎΡ‚Π½Π΅ΠΌΠ° Π΅Π΄Π½ΠΈ ΠΈ ΡΡŠΡ‰ΠΈ ΠΏΡ€ΠΈΠ±Π»ΠΈΠ·ΠΈΡ‚Π΅Π»Π½ΠΎ 4-5ms, Ρ‚ΠΎΠ³Π°Π²Π° цялата Π½ΠΈ ΠΏΠ΅Ρ‡Π°Π»Π±Π° във Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ -32% - Ρ‚ΠΎΠ²Π° Π΅ Π² Π½Π°ΠΉ-чист Π²ΠΈΠ΄ Π½Π°Ρ‚ΠΎΠ²Π°Ρ€Π²Π°Π½Π΅Ρ‚ΠΎ Π΅ ΠΏΡ€Π΅ΠΌΠ°Ρ…Π½Π°Ρ‚ΠΎ ΠΎΡ‚ основния процСсор, Π°ΠΊΠΎ Ρ‚Π°ΠΊΠ°Π²Π° заявка сС изпълнява Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΎ чСсто.

ΠšΠ°Ρ‚ΠΎ цяло Π½Π΅ трябва Π΄Π° ΠΏΡ€ΠΈΠ½ΡƒΠΆΠ΄Π°Π²Π°Ρ‚Π΅ основата Π΄Π° β€žΠ½ΠΎΡΠΈ ΠΊΡ€ΡŠΠ³Π»Π°Ρ‚Π°, Ρ‚ΡŠΡ€ΠΊΠ°Π»Ρ ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚Π½Π°Ρ‚Π°β€œ.

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: www.habr.com

ДобавянС Π½Π° Π½ΠΎΠ² ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€