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-เด•เตพ เด‡เดฒเตเดฒเต†เด™เตเด•เดฟเตฝ เดŽเดจเตเดคเตเด•เตŠเดฃเตเดŸเต เด‡เดตเดฟเดŸเต† VITH RECURSIVE เด†เดฏเดฟ?
  2. เด’เดฑเดฟเดœเดฟเดจเตฝ เดธเดพเดฎเตเดชเดฟเดณเตเดฎเดพเดฏเดฟ เดฌเดจเตเดงเดฟเดชเตเดชเดฟเดšเตเดšเดฟเดŸเตเดŸเตเดฃเตเดŸเต†เด™เตเด•เดฟเตฝ, เด’เดฐเต เดชเตเดฐเดคเตเดฏเต‡เด• CTE-เดฏเดฟเตฝ เดฎเดฟเดจเดฟ/เดฎเดพเด•เตเดธเต เดฎเต‚เดฒเตเดฏเด™เตเด™เตพ เด—เตเดฐเต‚เดชเตเดชเตเดšเต†เดฏเตเดฏเตเดจเตเดจเดคเต เดŽเดจเตเดคเตเด•เตŠเดฃเตเดŸเต?
    + 25% เดธเดฎเดฏเด‚
  3. เดฎเตเดฎเตเดชเดคเตเดคเต† CTE เด†เดตเตผเดคเตเดคเดฟเด•เตเด•เดพเตป เด…เดตเดธเดพเดจเด‚ เด‰เดชเดพเดงเดฟเด•เดณเดฟเดฒเตเดฒเดพเดคเตเดค 'SELECT * FROM' เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเดจเตเดจเดคเต เดŽเดจเตเดคเตเด•เตŠเดฃเตเดŸเต?
    + 14% เดธเดฎเดฏเด‚

เดˆ เดธเดพเดนเดšเดฐเตเดฏเดคเตเดคเดฟเตฝ, เดจเต†เดธเตเดฑเตเดฑเดกเต เดฒเต‚เดชเตเดชเดฒเตเดฒ, เด•เดฃเด•เตเดทเดจเดพเดฏเดฟ เดนเดพเดทเต เดœเต‹เดฏเดฟเตป เดคเดฟเดฐเดžเตเดžเต†เดŸเตเดคเตเดคเดคเต เดžเด™เตเด™เตพ เดตเดณเดฐเต† เดญเดพเด—เตเดฏเดตเดพเดจเดพเดฏเดฟเดฐเตเดจเตเดจเต, เด•เดพเดฐเดฃเด‚ เด…เดชเตเดชเต‹เตพ เดžเด™เตเด™เตพเด•เตเด•เต เด’เดฐเต CTE เดธเตเด•เดพเตป เดชเดพเดธเต เดฎเดพเดคเตเดฐเดฎเดฒเตเดฒ, 10K เดฒเดญเดฟเด•เตเด•เตเดฎเดพเดฏเดฟเดฐเตเดจเตเดจเต!

CTE เดธเตเด•เดพเดจเดฟเดจเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเต เด•เตเดฑเดšเตเดšเตเด‡เดตเดฟเดŸเต† เดจเดพเด‚ เด…เดคเต เด“เตผเด•เตเด•เดฃเด‚ CTE เดธเตเด•เดพเตป, Seq เดธเตเด•เดพเตป เดชเต‹เดฒเต†เดฏเดพเดฃเต - เด…เดคเดพเดฏเดคเต, เด‡เตปเดกเต†เด•เตโ€Œเดธเดฟเด‚เด—เต เด‡เดฒเตเดฒ, เดชเด•เตเดทเต‡ เดชเต‚เตผเดฃเตเดฃเดฎเดพเดฏ เดคเดฟเดฐเดฏเตฝ เดฎเดพเดคเตเดฐเดฎเต‡ เด†เดตเดถเตเดฏเดฎเตเดณเตเดณเต‚ 10K x 0.3ms = 3000 เดฎเดฟ cte_max เดตเดดเดฟเดฏเตเดณเตเดณ เดธเตˆเด•เตเด•เดฟเดณเตเด•เตพเด•เตเด•เดพเดฏเดฟ เด…เดฅเดตเดพ 1K x 1.5ms = 1500 เดฎเดฟ cte_bind เดตเดดเดฟ เดฒเต‚เดชเตเดชเต เดšเต†เดฏเตเดฏเตเดฎเตเดชเต‹เตพ!
เดฏเดฅเดพเตผเดคเตเดฅเดคเตเดคเดฟเตฝ, เดซเดฒเดฎเดพเดฏเดฟ เดจเดฟเด™เตเด™เตพเด•เตเด•เต เดŽเดจเตเดคเดพเดฃเต เดฒเดญเดฟเด•เตเด•เต‡เดฃเตเดŸเดคเต? เด…เดคเต†, เดธเดพเดงเดพเดฐเดฃเดฏเดพเดฏเดฟ เด‡เดคเต "เดฎเต‚เดจเตเดจเต-เดจเดฟเดฒ" เด…เดจเตเดตเต‡เดทเดฃเด™เตเด™เตพ เดตเดฟเดถเด•เดฒเดจเด‚ เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเดฟเดจเตเดฑเต† เด…เดžเตเดšเดพเด‚ เดฎเดฟเดจเดฟเดฑเตเดฑเดฟเตฝ เดŽเดตเดฟเดŸเต†เดฏเต‹ เดตเดฐเตเดจเตเดจ เดšเต‹เดฆเตเดฏเดฎเดพเดฃเต.

เด“เดฐเต‹ เด…เดฆเตเดตเดฟเดคเต€เดฏ เด•เต€ เดœเต‹เดกเดฟเด•เตเด•เตเด‚ เด”เดŸเตเดŸเตเดชเตเดŸเตเดŸเต เดšเต†เดฏเตเดฏเดพเตป เดžเด™เตเด™เตพ เด†เด—เตเดฐเดนเดฟเดšเตเดšเต เด•เต€_เดŽ เดชเตเดฐเด•เดพเดฐเด‚ เด—เตเดฐเต‚เดชเตเดชเดฟเตฝ เดจเดฟเดจเตเดจเต เดฎเดฟเดจเดฟเดฑเตเดฑเต/เดชเดฐเดฎเดพเดตเดงเดฟ.
เด…เดคเตเด•เตŠเดฃเตเดŸเต เดจเดฎเตเด•เตเด•เต เด‡เดคเต เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เดพเด‚ เดตเดฟเตปเดกเต‹ เดชเตเดฐเดตเตผเดคเตเดคเดจเด™เตเด™เตพ:

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-5 เดŽเด‚เดŽเดธเต เดŽเดŸเตเด•เตเด•เตเดจเตเดจเดคเดฟเดจเดพเตฝ, เดžเด™เตเด™เดณเตเดŸเต† เดธเดฎเดฏเดฎเต†เดฒเตเดฒเดพเด‚ เดฒเดพเดญเดฟเด•เตเด•เตเด‚ -32% - เด‡เดคเต เด…เดคเดฟเดจเตเดฑเต† เดถเตเดฆเตเดงเดฎเดพเดฏ เดฐเต‚เดชเดคเตเดคเดฟเดฒเดพเดฃเต เด…เดŸเดฟเดธเตเดฅเดพเดจ เดธเดฟเดชเดฟเดฏเตเดตเดฟเตฝ เดจเดฟเดจเตเดจเต เดฒเต‹เดกเต เดจเต€เด•เตเด•เด‚ เดšเต†เดฏเตเดคเต, เด…เดคเตเดคเดฐเดฎเตŠเดฐเต เด…เดญเตเดฏเตผเดคเตเดฅเดจ เดชเดฒเดชเตเดชเต‹เดดเตเด‚ เดจเดŸเดชเตเดชเดฟเดฒเดพเด•เตเด•เดฟเดฏเดพเตฝ เดฎเดคเดฟ.

เดชเตŠเดคเตเดตเต‡, "เดตเตƒเดคเตเดคเดพเด•เตƒเดคเดฟเดฏเดฟเดฒเตเดณเตเดณเดคเต เด•เตŠเดฃเตเดŸเตเดชเต‹เด•เตเด•, เดšเดคเตเดฐเด‚ เด‰เดฐเตเดŸเตเดŸเตเด•" เดŽเดจเตเดจเต เดจเดฟเด™เตเด™เตพ เด…เดŸเดฟเดคเตเดคเดฑเดฏเต† เดจเดฟเตผเดฌเดจเตเดงเดฟเด•เตเด•เดฐเตเดคเต.

เด…เดตเดฒเด‚เดฌเด‚: www.habr.com

เด’เดฐเต เด…เดญเดฟเดชเตเดฐเดพเดฏเด‚ เดšเต‡เตผเด•เตเด•เตเด•