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[explain.tensor.ru рдкрд╣рд╛]

рддреБрдХрдбреНрдпрд╛ рддреБрдХрдбреНрдпрд╛рдиреЗ рд╡реЗрдЧрд│реЗ рдХрд░рдгреЗ

рдЪрд▓рд╛ рд╡рд┐рдирдВрддреАрдХрдбреЗ рдЬрд╡рд│реВрди рдкрд╛рд╣реВ рдЖрдгрд┐ рдЧреЛрдВрдзрд│реВрди рдЬрд╛рдК:

  1. рдЬрд░ рд░рд┐рдХрд░реНрд╕рд┐рд╡ CTE рдирд╕рддреАрд▓ рддрд░ WITH RECURSIVE рдЗрдереЗ рдХрд╛ рдЖрд╣реЗ?
  2. рд╡реЗрдЧрд│реНрдпрд╛ CTE рдордзреАрд▓ рдХрд┐рдорд╛рди/рдЕрдзрд┐рдХрддрдо рдореВрд▓реНрдпреЗ рдореВрд│ рдирдореБрдиреНрдпрд╛рд╢реА рдЬреЛрдбрд▓реА рдЧреЗрд▓реНрдпрд╛рд╕ рддреА рдХрд╛ рдЧрдЯ рдХрд░рд╛рдпрдЪреА?
    +25% рд╡реЗрд│
  3. рдорд╛рдЧреАрд▓ CTE рдЪреА рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╢реЗрд╡рдЯреА рдмрд┐рдирд╢рд░реНрдд 'SELECT * FROM' рдХрд╛ рд╡рд╛рдкрд░рд╛рд╡реЗ?
    +14% рд╡реЗрд│

рдпрд╛ рдкреНрд░рдХрд░рдгрд╛рдд, рдЖрдореНрд╣реА рдЦреВрдк рднрд╛рдЧреНрдпрд╡рд╛рди рдЖрд╣реЛрдд рдХреА рдХрдиреЗрдХреНрд╢рдирд╕рд╛рдареА рд╣реЕрд╢ рдЬреЙрдИрди рдирд┐рд╡рдбрд▓реЗ рдЧреЗрд▓реЗ рд╣реЛрддреЗ, рдиреЗрд╕реНрдЯреЗрдб рд▓реВрдк рдирд╛рд╣реА, рдХрд╛рд░рдг рддреЗрд╡реНрд╣рд╛ рдЖрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд рдПрдХ CTE рд╕реНрдХреЕрди рдкрд╛рд╕ рдорд┐рд│рд╛рд▓рд╛ рдирд╕рддрд╛ рддрд░ 10K!

CTE рд╕реНрдХреЕрди рдмрджреНрджрд▓ рдереЛрдбреЗрд╕реЗрдпреЗрдереЗ рдЖрдкрдг рддреЗ рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд▓реЗ рдкрд╛рд╣рд┐рдЬреЗ рд╕реАрдЯреАрдИ рд╕реНрдХреЕрди рд╣реЗ рд╕реЗрдХ рд╕реНрдХреЕрдирд╕рд╛рд░рдЦреЗрдЪ рдЖрд╣реЗ - рдореНрд╣рдгрдЬреЗ, рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рдирд╛рд╣реА, рдкрд░рдВрддреБ рдХреЗрд╡рд│ рдПрдХ рд╕рдВрдкреВрд░реНрдг рд╢реЛрдз, рдЬреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЕрд╕реЗрд▓ 10K x 0.3ms = 3000ms cte_max рджреНрд╡рд╛рд░реЗ рд╕рд╛рдпрдХрд▓рд╕рд╛рдареА рдХрд┐рдВрд╡рд╛ 1K x 1.5ms = 1500ms cte_bind рджреНрд╡рд╛рд░реЗ рд▓реВрдк рдХрд░рддрд╛рдирд╛!
рд╡рд╛рд╕реНрддрд╡рд┐рдХ, рддреБрдореНрд╣рд╛рд▓рд╛ рдкрд░рд┐рдгрд╛рдо рдореНрд╣рдгреВрди рдХрд╛рдп рдорд┐рд│рд╡рд╛рдпрдЪреЗ рд╣реЛрддреЗ? рд╣реЛрдп, рд╕рд╣рд╕рд╛ рд╣рд╛ рдкреНрд░рд╢реНрди "рддреАрди-рдордЬрд▓реА" рдкреНрд░рд╢реНрдирд╛рдВрдЪреЗ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рддрд╛рдирд╛ 5 рд╡реНрдпрд╛ рдорд┐рдирд┐рдЯрд╛рд▓рд╛ рдХреБрдареЗрддрд░реА рдпреЗрддреЛ.

рдЖрдореНрд╣рд╛рд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ рдЕрджреНрд╡рд┐рддреАрдп рдХреА рдЬреЛрдбреАрд╕рд╛рдареА рдЖрдЙрдЯрдкреБрдЯ рдХрд░рд╛рдпрдЪреЗ рд╣реЛрддреЗ 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
[explain.tensor.ru рдкрд╣рд╛]

рджреЛрдиреНрд╣реА рдкрд░реНрдпрд╛рдпрд╛рдВрдордзреНтАНрдпреЗ рдбреЗрдЯрд╛ рд╡рд╛рдЪрдгреНтАНрдпрд╛рд╕рд╛рдареА рдЕрдВрджрд╛рдЬреЗ 4-5ms рдЗрддрдХрд╛рдЪ рд╡реЗрд│ рд▓рд╛рдЧрддреЛ, рдордЧ рдЖрдордЪрд╛ рд╕рд░реНрд╡ рд╡реЗрд│ рд╡рд╛рдврддреЛ -32% - рд╣реЗ рддреНрдпрд╛рдЪреНрдпрд╛ рд╢реБрджреНрдз рд╕реНрд╡рд░реВрдкрд╛рдд рдЖрд╣реЗ рдмреЗрд╕ CPU рд╡рд░реВрди рд▓реЛрдб рдХрд╛рдврд▓реЗ, рдЬрд░ рдЕрд╢реА рд╡рд┐рдирдВрддреА рдкреБрд░реЗрд╢реА рдЕрдВрдорд▓рд╛рдд рдЖрдгрд▓реА рдЬрд╛рддреЗ.

рд╕рд░реНрд╡рд╕рд╛рдзрд╛рд░рдгрдкрдгреЗ, рддреБрдореНрд╣реА рдмреЗрд╕рд▓рд╛ "рдЧреЛрд▓рд╛рдХрд╛рд░ рдШреЗрдКрди рдЬрд╛, рдЪреМрдХреЛрдиреА рд░реЛрд▓ рдХрд░рд╛" рдЕрд╢реА рд╕рдХреНрддреА рдХрд░реВ рдирдпреЗ.

рд╕реНрддреНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╛