PostgreSQL Antipatterns: ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืฉืœืึธื’ืŸ ืฉื•ื•ืขืจ JOIN

ืžื™ืจ ืคืึธืจื–ืขืฆืŸ ื“ื™ ืกืขืจื™ืข ืคื•ืŸ โ€‹โ€‹โ€‹โ€‹ืึทืจื˜ื™ืงืœืขืŸ ื’ืขื˜ืจื™ื™ึท ืฆื• ื“ื™ ืœืขืจื ืขืŸ ืคื•ืŸ ืงืœื™ื™ืŸ-ื‘ืืงืื ื˜ ื•ื•ืขื’ืŸ ืฆื• ืคึฟืึทืจื‘ืขืกืขืจืŸ ื“ื™ ืคืึธืจืฉื˜ืขืœื•ื ื’ ืคื•ืŸ "ืคึผืึธื ืขื ืคึผืฉื•ื˜" ืคึผืึธืกื˜ื’ืจืขืกืงืœ ืงื•ื•ื™ืจื™ื–:

ื“ื• ื–ืืœืกื˜ ื ื™ืฉื˜ ื˜ืจืึทื›ื˜ืŸ ืึทื– ืื™ืš ื˜ืึธืŸ ื ื™ื˜ ื•ื•ื™ JIN ืึทื–ื•ื™ ืคื™ืœ ... :)

ืึธื‘ืขืจ ืึธืคื˜ ืึธืŸ ืขืก, ื“ื™ ื‘ืงืฉื” ื˜ื•ืจื ืก ืื•ื™ืก ืฆื• ื–ื™ื™ืŸ ื‘ืื˜ื™ื™ื˜ื™ืง ืžืขืจ ืคึผืจืึธื“ื•ืงื˜ื™ื•ื• ื•ื•ื™ ืžื™ื˜ ืื™ื. ืึทื–ื•ื™ ื”ื™ื™ึทื ื˜ ืžื™ืจ ื•ื•ืขืœืŸ ืคึผืจื•ื‘ื™ืจืŸ ื‘ืึทืงื•ืžืขืŸ ื‘ืึทืคืจื™ื™ึทืขืŸ ืคื•ืŸ ืžื™ื˜ืœ-ืื™ื ื˜ืขื ืกื™ื•ื•ืข JOIN - ื ื™ืฆืŸ ืึท ื•ื•ืขืจื˜ืขืจื‘ื•ืš.

PostgreSQL Antipatterns: ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืฉืœืึธื’ืŸ ืฉื•ื•ืขืจ JOIN

ืกื˜ืึทืจื˜ื™ื ื’ ืžื™ื˜ PostgreSQL 12, ืขื˜ืœืขื›ืข ืคื•ืŸ โ€‹โ€‹โ€‹โ€‹ื“ื™ ืกื™ื˜ื•ืึทื˜ื™ืึธื ืก ื“ื™ืกืงืจื™ื™ื‘ื“ ืื•ื ื˜ืŸ ืงืขืŸ ื–ื™ื™ืŸ ืจื™ืคึผืจืึทื“ื•ืกื˜ ืึท ื‘ื™ืกืœ ืึทื ื“ืขืจืฉ ื•ื•ื™ื™ึทืœ ืคื•ืŸ ืคืขืœื™ืงื™ื™ึทื˜ ื ื™ื˜-ืžืึทื˜ื™ืจื™ืึทืœื™ื–ื™ื™ืฉืึทืŸ CTE. ื“ืขื ืึธืคึผืคื™ืจื•ื ื’ ืงืขื ืขืŸ ื–ื™ื™ืŸ ืจื™ื•ื•ืขืจื˜ื™ื“ ื“ื•ืจืš ืกืคึผืขืฆื™ืคื™ืฆื™ืจืŸ ื“ื™ ืฉืœื™ืกืœ MATERIALIZED.

ืคื™ืœืข "ืคืืงื˜ืŸ" ืื™ืŸ ืึท ืœื™ืžื™ื˜ืขื“ ื•ื•ืึธืงืึทื‘ื•ืœืึทืจื™

ืœืึธืžื™ืจ ื ืขืžืขืŸ ืึท ื–ื™ื™ืขืจ ืคืึทืงื˜ื™ืฉ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืึทืจื‘ืขื˜ - ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื•ื•ื™ื™ึทื–ืŸ ืึท ืจืฉื™ืžื” ื™ื ืงืึทืžื™ื ื’ ืึทืจื˜ื™ืงืœืขืŸ ืึธื“ืขืจ ืึทืงื˜ื™ื•ื• ื˜ืึทืกืงืก ืžื™ื˜ ืกืขื ื“ืขืจืก:

25.01 | ะ˜ะฒะฐะฝะพะฒ ะ˜.ะ˜. | ะŸะพะดะณะพั‚ะพะฒะธั‚ัŒ ะพะฟะธัะฐะฝะธะต ะฝะพะฒะพะณะพ ะฐะปะณะพั€ะธั‚ะผะฐ.
22.01 | ะ˜ะฒะฐะฝะพะฒ ะ˜.ะ˜. | ะะฐะฟะธัะฐั‚ัŒ ัั‚ะฐั‚ัŒัŽ ะฝะฐ ะฅะฐะฑั€: ะถะธะทะฝัŒ ะฑะตะท JOIN.
20.01 | ะŸะตั‚ั€ะพะฒ ะŸ.ะŸ. | ะŸะพะผะพั‡ัŒ ะพะฟั‚ะธะผะธะทะธั€ะพะฒะฐั‚ัŒ ะทะฐะฟั€ะพั.
18.01 | ะ˜ะฒะฐะฝะพะฒ ะ˜.ะ˜. | ะะฐะฟะธัะฐั‚ัŒ ัั‚ะฐั‚ัŒัŽ ะฝะฐ ะฅะฐะฑั€: JOIN ั ัƒั‡ะตั‚ะพะผ ั€ะฐัะฟั€ะตะดะตะปะตะฝะธั ะดะฐะฝะฝั‹ั….
16.01 | ะŸะตั‚ั€ะพะฒ ะŸ.ะŸ. | ะŸะพะผะพั‡ัŒ ะพะฟั‚ะธะผะธะทะธั€ะพะฒะฐั‚ัŒ ะทะฐะฟั€ะพั.

ืื™ืŸ ื“ืขืจ ืึทื‘ืกื˜ืจืึทืงื˜ ื•ื•ืขืœื˜, ืึทืจื‘ืขื˜ ืžื—ื‘ืจื™ื ื–ืึธืœ ื–ื™ื™ืŸ ื™ื•ื•ืึทื ืœื™ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืฆื•ื•ื™ืฉืŸ ืึทืœืข ืขืžืคึผืœื•ื™ื™ื– ืคื•ืŸ ืื•ื ื“ื–ืขืจ ืึธืจื’ืึทื ื™ื–ืึทืฆื™ืข, ืึธื‘ืขืจ ืื™ืŸ ืคืึทืงื˜ ื˜ืึทืกืงืก ืงื•ืžืขืŸ, ื•ื•ื™ ืึท ื”ืขืจืฉืŸ, ืคื•ืŸ ืึท ืคืขืจืœื™ ืœื™ืžื™ื˜ืขื“ ื ื•ืžืขืจ ืคื•ืŸ ืžืขื ื˜ืฉืŸ - "ืคื•ืŸ ืคืึทืจื•ื•ืึทืœื˜ื•ื ื’" ืึทืจื•ื™ืฃ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ ืึธื“ืขืจ "ืคื•ืŸ ืกืึทื‘ืงืึทื ื˜ืจืึทืงื˜ืขืจื–" ืคื•ืŸ ืืจื•ืžื™ืงืข ื“ื™ืคึผืึทืจื˜ืžืึทื ืฅ (ืึทื ืึทืœื™ืก, ื“ื™ื–ื™ื™ื ืขืจื–, ืคึฟืึทืจืงื•ื™ืฃ, ...).

ืœืึธืžื™ืจ ืึธื ื ืขืžืขืŸ ืึทื– ืื™ืŸ ืื•ื ื“ื–ืขืจ ืึธืจื’ืึทื ื™ื–ืึทืฆื™ืข ืคื•ืŸ โ€‹โ€‹1000 ืžืขื ื˜ืฉืŸ, ื‘ืœื•ื™ื– 20 ืžื—ื‘ืจื™ื (ื™ื•ื–ืฉืึทื•ื•ืึทืœื™ ืืคื™ืœื• ื•ื•ื™ื™ื ื™ืงืขืจ) ืฉื˜ืขืœืŸ ื˜ืึทืกืงืก ืคึฟืึทืจ ื™ืขื“ืขืจ ืกืคึผืขืฆื™ืคื™ืฉ ืคึผืขืจืคืึธืจืžืขืจ ืื•ืŸ ื–ืืœ ืก ื ื•ืฆืŸ ื“ืขื ื•ื ื˜ืขืจื˜ืขื ื™ืง ื•ื•ื™ืกืŸืฆื• ืคืึทืจื’ื™ื›ืขืจืŸ ื“ื™ "ื‘ืขืงืึทื‘ืึธืœืขื“ื™ืง" ืึธื ืคึฟืจืขื’.

ืกืงืจื™ืคึผื˜ ื’ืขื ืขืจืึทื˜ืึธืจ

-- ัะพั‚ั€ัƒะดะฝะธะบะธ
CREATE TABLE person AS
SELECT
  id
, repeat(chr(ascii('a') + (id % 26)), (id % 32) + 1) "name"
, '2000-01-01'::date - (random() * 1e4)::integer birth_date
FROM
  generate_series(1, 1000) id;

ALTER TABLE person ADD PRIMARY KEY(id);

-- ะทะฐะดะฐั‡ะธ ั ัƒะบะฐะทะฐะฝะฝั‹ะผ ั€ะฐัะฟั€ะตะดะตะปะตะฝะธะตะผ
CREATE TABLE task AS
WITH aid AS (
  SELECT
    id
  , array_agg((random() * 999)::integer + 1) aids
  FROM
    generate_series(1, 1000) id
  , generate_series(1, 20)
  GROUP BY
    1
)
SELECT
  *
FROM
  (
    SELECT
      id
    , '2020-01-01'::date - (random() * 1e3)::integer task_date
    , (random() * 999)::integer + 1 owner_id
    FROM
      generate_series(1, 100000) id
  ) T
, LATERAL(
    SELECT
      aids[(random() * (array_length(aids, 1) - 1))::integer + 1] author_id
    FROM
      aid
    WHERE
      id = T.owner_id
    LIMIT 1
  ) a;

ALTER TABLE task ADD PRIMARY KEY(id);
CREATE INDEX ON task(owner_id, task_date);
CREATE INDEX ON task(author_id);

ืœืึธืžื™ืจ ื•ื•ื™ื™ึทื–ืŸ ื“ื™ ืœืขืฆื˜ืข 100 ื˜ืึทืกืงืก ืคึฟืึทืจ ืึท ืกืคึผืขืฆื™ืคื™ืฉ ืขืงืกืึทืงื™ื•ื˜ืขืจ:

SELECT
  task.*
, person.name
FROM
  task
LEFT JOIN
  person
    ON person.id = task.author_id
WHERE
  owner_id = 777
ORDER BY
  task_date DESC
LIMIT 100;

PostgreSQL Antipatterns: ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืฉืœืึธื’ืŸ ืฉื•ื•ืขืจ JOIN
[ืงื•ืง ืื•ื™ืฃ explain.tensor.ru]

ืขืก ื˜ื•ืจื ืก ืื•ื™ืก ืึทื– 1/3 ื’ืึทื ืฅ ืฆื™ื™ื˜ ืื•ืŸ 3/4 ืจื™ื“ื™ื ื’ื– ื‘ืœืขื˜ืขืจ ืคื•ืŸ ื“ืึทื˜ืŸ ื–ืขื ืขืŸ ื’ืขืžืื›ื˜ ื‘ืœื•ื™ื– ืฆื• ื–ื•ื›ืŸ ืคึฟืึทืจ ื“ืขืจ ืžื—ื‘ืจ 100 ืžืืœ - ืคึฟืึทืจ ื™ืขื“ืขืจ ืจืขื–ื•ืœื˜ืึทื˜ ืึทืจื‘ืขื˜. ืื‘ืขืจ ืžื™ืจ ื•ื•ื™ืกืŸ ืึทื– ืฆื•ื•ื™ืฉืŸ ื“ื™ ื”ื•ื ื“ืขืจื˜ืขืจ ื‘ืœื•ื™ื– 20 ืคืึทืจืฉื™ื“ืขื ืข - ืื™ื– ืขืก ืžืขื’ืœืขืš ืฆื• ื ื•ืฆืŸ ื“ืขื ื•ื•ื™ืกืŸ?

ื”ืกื˜ืึธืจืข-ื•ื•ืขืจื˜ืขืจื‘ื•ืš

ื–ืืœ ืก ื ืขืžืขืŸ ืžื™ื™ึทืœืข ื”ืกื˜ืึธืจืข ื˜ื™ืคึผ ืฆื• ื“ื–ืฉืขื ืขืจื™ื™ื˜ ืึท "ื•ื•ืขืจื˜ืขืจื‘ื•ืš" ืฉืœื™ืกืœ ื•ื•ืขืจื˜:

CREATE EXTENSION hstore

ืžื™ืจ ื ืึธืจ ื“ืึทืจืคึฟืŸ ืฆื• ืฉื˜ืขืœืŸ ื“ืขื ืžื—ื‘ืจ ืก ืฉื™ื™ึทืŸ ืื•ืŸ ื–ื™ื™ืŸ ื ืึธืžืขืŸ ืื™ืŸ ื“ืขื ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืึทื–ื•ื™ ืึทื– ืžื™ืจ ืงืขื ืขืŸ ืขืงืกื˜ืจืึทืงื˜ ืžื™ื˜ ื“ืขื ืฉืœื™ืกืœ:

-- ั„ะพั€ะผะธั€ัƒะตะผ ั†ะตะปะตะฒัƒัŽ ะฒั‹ะฑะพั€ะบัƒ
WITH T AS (
  SELECT
    *
  FROM
    task
  WHERE
    owner_id = 777
  ORDER BY
    task_date DESC
  LIMIT 100
)
-- ั„ะพั€ะผะธั€ัƒะตะผ ัะปะพะฒะฐั€ัŒ ะดะปั ัƒะฝะธะบะฐะปัŒะฝั‹ั… ะทะฝะฐั‡ะตะฝะธะน
, dict AS (
  SELECT
    hstore( -- hstore(keys::text[], values::text[])
      array_agg(id)::text[]
    , array_agg(name)::text[]
    )
  FROM
    person
  WHERE
    id = ANY(ARRAY(
      SELECT DISTINCT
        author_id
      FROM
        T
    ))
)
-- ะฟะพะปัƒั‡ะฐะตะผ ัะฒัะทะฐะฝะฝั‹ะต ะทะฝะฐั‡ะตะฝะธั ัะปะพะฒะฐั€ั
SELECT
  *
, (TABLE dict) -> author_id::text -- hstore -> key
FROM
  T;

PostgreSQL Antipatterns: ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืฉืœืึธื’ืŸ ืฉื•ื•ืขืจ JOIN
[ืงื•ืง ืื•ื™ืฃ explain.tensor.ru]

ืคืืจื‘ืจืื›ื˜ ืื•ื™ืฃ ืงืจื™ื’ืŸ ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ืžืขื ื˜ืฉืŸ 2 ืžืืœ ื•ื•ื™ื™ื ื™ืงืขืจ ืฆื™ื™ื˜ ืื•ืŸ 7 ืžืืœ ื•ื•ื™ื™ื ื™ืงืขืจ ื“ืึทื˜ืŸ ืœื™ื™ืขื ืขืŸ! ืื™ืŸ ื“ืขืจืฆื• ืฆื• "ื•ื•ืึธืงืึทื‘ื•ืœืึทืจื™", ื•ื•ืึธืก ืื•ื™ืš ื’ืขื”ืึธืœืคึฟืŸ ืื•ื ื“ื– ื“ืขืจื’ืจื™ื™ื›ืŸ ื“ื™ ืจืขื–ื•ืœื˜ืึทื˜ืŸ ืื™ื– ื’ืขื•ื•ืขืŸ ืคืึทืจื ืขื ืจืขืงืึธืจื“ ืจื™ื˜ืจื™ื•ื•ืึทืœ ืคื•ืŸ ื“ื™ ื˜ื™ืฉ ืื™ืŸ ืึท ืื™ื™ืŸ ืคืึธืจืŸ ื ื™ืฆืŸ = ANY(ARRAY(...)).

ื˜ื™ืฉ ืื™ื™ื ืกืŸ: ืกื™ืจื™ืึทืœื™ื–ืึทื˜ื™ืึธืŸ ืื•ืŸ ื“ืขืกืขืจื™ืึทืœื™ื–ืึทื˜ื™ืึธืŸ

ืึธื‘ืขืจ ื•ื•ืึธืก ืื•ื™ื‘ ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืจืึทื˜ืขื•ื•ืขืŸ ื ื™ืฉื˜ ื‘ืœื•ื™ื– ืื™ื™ืŸ ื˜ืขืงืกื˜ ืคืขืœื“, ืึธื‘ืขืจ ืึท ื’ืึทื ืฅ ืคึผืึธื–ื™ืฆื™ืข ืื™ืŸ ื“ืขื ื•ื•ืขืจื˜ืขืจื‘ื•ืš? ืื™ืŸ ื“ืขื ืคืึทืœ, ื“ื™ ืคื™ื™ื™ืงื™ื™ื˜ ืคื•ืŸ PostgreSQL ื•ื•ืขื˜ ื”ืขืœืคึฟืŸ ืื•ื ื“ื– ื‘ืึทื”ืึทื ื“ืœืขืŸ ืึท ื˜ื™ืฉ ืคึผืึธื–ื™ืฆื™ืข ื•ื•ื™ ืึท ืื™ื™ืŸ ื•ื•ืขืจื˜:

...
, dict AS (
  SELECT
    hstore(
      array_agg(id)::text[]
    , array_agg(p)::text[] -- ะผะฐะณะธั #1
    )
  FROM
    person p
  WHERE
    ...
)
SELECT
  *
, (((TABLE dict) -> author_id::text)::person).* -- ะผะฐะณะธั #2
FROM
  T;

ื–ืืœ ืก ืงื•ืง ืื™ืŸ ื•ื•ืึธืก ืื™ื– ื’ืขื•ื•ืขืŸ ื’ืขืฉืขืขื ื™ืฉ ื“ืึธ:

  1. ืžื™ืจ ื’ืขื ื•ืžืขืŸ p ื•ื•ื™ ืึทืŸ ืึทืœื™ืึทืก ืฆื• ื“ื™ ืคื•ืœ ืžืขื ื˜ืฉ ื˜ื™ืฉ ืคึผืึธื–ื™ืฆื™ืข ืื•ึผืŸ ื”ึธืื˜ ื–ึตื™ื™ ื’ึดื™ื–ึธืื’ึฐื˜ ืึท ื’ึทืื ึฐื’ึถืขืŸ.
  2. ื“ืขื ื“ื™ ืžืขื ื’ืข ืคื•ืŸ โ€‹โ€‹ืจืขืงืึธืจื“ื™ื ื’ืก ืื™ื– ื’ืขื•ื•ืขืŸ ืจื™ืงืึทืกื˜ ืฆื• ืึท ืžืขื ื’ืข ืคื•ืŸ โ€‹โ€‹ื˜ืขืงืกื˜ ืกื˜ืจื™ื ื’ืก (ืžืขื ื˜ืฉ [] :: ื˜ืขืงืกื˜ []) ืฆื• ืฉื˜ืขืœืŸ ืขืก ืื™ืŸ ื“ื™ Hstore ื•ื•ืขืจื˜ืขืจื‘ื•ืš ื•ื•ื™ ืึท ืžืขื ื’ืข ืคื•ืŸ โ€‹โ€‹ื•ื•ืึทืœื•ืขืก.
  3. ื•ื•ืขืŸ ืžื™ืจ ื‘ืึทืงื•ืžืขืŸ ืึท ืฉื™ื™ึทื›ื•ืช ืจืขืงืึธืจื“, ืžื™ืจ ืคึผื•ืœื“ ืคื•ืŸ ื“ื™ ื•ื•ืขืจื˜ืขืจื‘ื•ืš ื“ื•ืจืš ืฉืœื™ืกืœ ื•ื•ื™ ืึท ื˜ืขืงืกื˜ ืฉื˜ืจื™ืงืœ.
  4. ืžื™ืจ ื“ืึทืจืคึฟืŸ ื˜ืขืงืกื˜ ื•ื•ืขื ื“ืŸ ืื™ืŸ ืึท ื˜ื™ืฉ ื˜ื™ืคึผ ื•ื•ืขืจื˜ ืžืขื ื˜ืฉ (ืคึฟืึทืจ ื™ืขื“ืขืจ ื˜ื™ืฉ ืึท ื˜ื™ืคึผ ืคื•ืŸ ื“ื™ ื–ืขืœื‘ืข ื ืึธืžืขืŸ ืื™ื– ืื•ื™ื˜ืึธืžืึทื˜ื™ืฉ ื‘ืืฉืืคืŸ).
  5. "ืขืงืกืคึผืึทื ื“" ื“ื™ ื˜ื™ื™ืคึผื˜ ืจืขืงืึธืจื“ ืื™ืŸ ืฉืคืืœื˜ืŸ ื ื™ืฆืŸ (...).*.

json ื•ื•ืขืจื˜ืขืจื‘ื•ืš

ืื‘ืขืจ ืึทื–ืึท ืึท ื˜ืจื™ืง ื•ื•ื™ ืžื™ืจ ื’ืขื•ื•ืขื ื“ื˜ ืื•ื™ื‘ืŸ ื•ื•ืขื˜ ื ื™ืฉื˜ ืึทืจื‘ืขื˜ืŸ ืื•ื™ื‘ ืขืก ืื™ื– ืงื™ื™ืŸ ืงืึธืจืึทืกืคึผืึทื ื“ื™ื ื’ ื˜ื™ืฉ ื˜ื™ืคึผ ืฆื• ื˜ืึธืŸ ื“ื™ "ืงืึทืกื˜ื™ื ื’". ืคึผื•ื ืงื˜ ื“ืขืจ ื–ืขืœื‘ื™ืงืขืจ ืกื™ื˜ื•ืึทืฆื™ืข ื•ื•ืขื˜ ืื•ื™ืคืฉื˜ื™ื™ืŸ, ืื•ืŸ ืื•ื™ื‘ ืžื™ืจ ืคึผืจื•ื‘ื™ืจืŸ ืฆื• ื ื•ืฆืŸ ืึท CTE ืจื•ื“ืขืจืŸ, ื ื™ืฉื˜ ืึท "ืคืึทืงื˜ื™ืฉ" ื˜ื™ืฉ.

ืื™ืŸ ื“ืขื ืคืึทืœ ื–ื™ื™ ื•ื•ืขืœืŸ ื”ืขืœืคืŸ ืื•ื ื“ื– ืคืึทื ื’ืงืฉืึทื ื– ืคึฟืึทืจ ืืจื‘ืขื˜ืŸ ืžื™ื˜ json:

...
, p AS ( -- ัั‚ะพ ัƒะถะต CTE
  SELECT
    *
  FROM
    person
  WHERE
    ...
)
, dict AS (
  SELECT
    json_object( -- ั‚ะตะฟะตั€ัŒ ัั‚ะพ ัƒะถะต json
      array_agg(id)::text[]
    , array_agg(row_to_json(p))::text[] -- ะธ ะฒะฝัƒั‚ั€ะธ json ะดะปั ะบะฐะถะดะพะน ัั‚ั€ะพะบะธ
    )
  FROM
    p
)
SELECT
  *
FROM
  T
, LATERAL(
    SELECT
      *
    FROM
      json_to_record(
        ((TABLE dict) ->> author_id::text)::json -- ะธะทะฒะปะตะบะปะธ ะธะท ัะปะพะฒะฐั€ั ะบะฐะบ json
      ) AS j(name text, birth_date date) -- ะทะฐะฟะพะปะฝะธะปะธ ะฝัƒะถะฝัƒัŽ ะฝะฐะผ ัั‚ั€ัƒะบั‚ัƒั€ัƒ
  ) j;

ืขืก ื–ืึธืœ ื–ื™ื™ืŸ ืื ื’ืขื•ื•ื™ื–ืŸ ืึทื– ื•ื•ืขืŸ ื“ื™ืกืงืจื™ื™ื‘ื™ื ื’ ื“ื™ ืฆื™ืœ ืกื˜ืจื•ืงื˜ื•ืจ, ืžื™ืจ ืงืขื ืขืŸ ื ื™ืฉื˜ ืจืฉื™ืžื” ืึทืœืข ื“ื™ ืคืขืœื“ืขืจ ืคื•ืŸ ื“ื™ ืžืงื•ืจ ืฉื˜ืจื™ืงืœ, ืึธื‘ืขืจ ื‘ืœื•ื™ื– ื“ื™ ื•ื•ืึธืก ืžื™ืจ ื˜ืึทืงืข ื“ืึทืจืคึฟืŸ. ืื•ื™ื‘ ืžื™ืจ ื”ืึธื‘ืŸ ืึท "ื’ืขื‘ื•ื™ืจืŸ" ื˜ื™ืฉ, ืขืก ืื™ื– ื‘ืขืกืขืจ ืฆื• ื ื•ืฆืŸ ื“ื™ ืคึฟื•ื ืงืฆื™ืข json_populate_record.

ืžื™ืจ ื ืึธืš ืึทืงืกืขืก ื“ืขื ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืึทืžืึธืœ, ืึธื‘ืขืจ json-[de] ืกื™ืจื™ืึทืœื™ื–ื™ื™ืฉืึทืŸ ืงืึธืก ื–ืขื ืขืŸ ื’ืึทื ืฅ ื”ื•ื™ืš, ื“ืขืจื™ื‘ืขืจ, ืขืก ืื™ื– ื’ืœื™ื™ึทืš ืฆื• ื ื•ืฆืŸ ื“ืขื ืื•ืคึฟืŸ ื‘ืœื•ื™ื– ืื™ืŸ ืขื˜ืœืขื›ืข ืงืึทืกืขืก ื•ื•ืขืŸ ื“ื™ "ืขืจืœืขืš" CTE ืกืงืึทืŸ ืื™ื– ืขืจื’ืขืจ.

ื˜ืขืกื˜ื™ื ื’ ืคืึธืจืฉื˜ืขืœื•ื ื’

ืึทื–ื•ื™, ืžื™ืจ ื”ืึธื‘ืŸ ืฆื•ื•ื™ื™ ื•ื•ืขื’ืŸ ืฆื• ืกื™ืจื™ืึทืœื™ื–ื™ืจืŸ ื“ืึทื˜ืŸ ืื™ืŸ ืึท ื•ื•ืขืจื˜ืขืจื‘ื•ืš - hstore/json_object. ืื™ืŸ ืึทื“ื™ืฉืึทืŸ, ื“ื™ ืขืจื™ื™ื– ืคื•ืŸ ืฉืœื™ืกืœืขืŸ ืื•ืŸ ื•ื•ืึทืœื•ืขืก ื–ื™ืš ืงืขื ืขืŸ ืื•ื™ืš ื–ื™ื™ืŸ ื“ื–ืฉืขื ืขืจื™ื™ื˜ืึทื“ ืื™ืŸ ืฆื•ื•ื™ื™ ื•ื•ืขื’ืŸ, ืžื™ื˜ ื™ื ืขืจืœืขืš ืึธื“ืขืจ ืคื•ื ื“ืจื•ื™ืกื ื“ื™ืง ืงืึทื ื•ื•ืขืจื–ืฉืึทืŸ ืฆื• ื˜ืขืงืกื˜: array_agg(i::text) / array_agg(i)::text[].

ืœืึธืžื™ืจ ืงืึธื ื˜ืจืึธืœื™ืจืŸ ื“ื™ ื™ืคืขืงื˜ื™ื•ื•ื ืึทืก ืคื•ืŸ ืคืึทืจืฉื™ื“ืขื ืข ื˜ื™ื™ืคึผืก ืคื•ืŸ ืกื™ืจื™ืึทืœื™ื–ื™ื™ืฉืึทืŸ ืžื™ื˜ ืึท ืจื™ื™ืŸ ืกื™ื ื˜ืขื˜ื™ืฉ ื‘ื™ื™ึทืฉืคึผื™ืœ - ืกื™ืจื™ืึทืœื™ื– ืคืึทืจืฉื™ื“ืขื ืข ื ื•ืžืขืจืŸ ืคื•ืŸ ืฉืœื™ืกืœืขืŸ:

WITH dict AS (
  SELECT
    hstore(
      array_agg(i::text)
    , array_agg(i::text)
    )
  FROM
    generate_series(1, ...) i
)
TABLE dict;

ืืคืฉืืฆื•ื ื’ ืฉืจื™ืคื˜: ืกื™ืจื™ืึทืœื™ื–ืึทื˜ื™ืึธืŸ

WITH T AS (
  SELECT
    *
  , (
      SELECT
        regexp_replace(ea[array_length(ea, 1)], '^Execution Time: (d+.d+) ms$', '1')::real et
      FROM
        (
          SELECT
            array_agg(el) ea
          FROM
            dblink('port= ' || current_setting('port') || ' dbname=' || current_database(), $$
              explain analyze
              WITH dict AS (
                SELECT
                  hstore(
                    array_agg(i::text)
                  , array_agg(i::text)
                  )
                FROM
                  generate_series(1, $$ || (1 << v) || $$) i
              )
              TABLE dict
            $$) T(el text)
        ) T
    ) et
  FROM
    generate_series(0, 19) v
  ,   LATERAL generate_series(1, 7) i
  ORDER BY
    1, 2
)
SELECT
  v
, avg(et)::numeric(32,3)
FROM
  T
GROUP BY
  1
ORDER BY
  1;

PostgreSQL Antipatterns: ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืฉืœืึธื’ืŸ ืฉื•ื•ืขืจ JOIN

ืื•ื™ืฃ PostgreSQL 11, ืึทืจื•ื™ืฃ ืฆื• ื‘ืขืขืจืขืš ืึท ื•ื•ืขืจื˜ืขืจื‘ื•ืš ื’ืจื™ื™ืก ืคื•ืŸ 2 ^ 12 ืฉืœื™ืกืœืขืŸ ืกื™ืจื™ืึทืœื™ื–ื™ื™ืฉืึทืŸ ืฆื• ื“ื–ืฉืกืึธืŸ ื ืขืžื˜ ื•ื•ื™ื™ื ื™ืงืขืจ ืฆื™ื™ื˜. ืื™ืŸ ื“ืขื ืคืึทืœ, ื“ื™ ืžืขืจืกื˜ ืขืคืขืงื˜ื™ื•ื• ืื™ื– ื“ื™ ืงืึธืžื‘ื™ื ืึทืฆื™ืข ืคื•ืŸ โ€‹โ€‹json_object ืื•ืŸ "ื™ื ืขืจืœืขืš" ื˜ื™ืคึผ ืงืึทื ื•ื•ืขืจื–ืฉืึทืŸ array_agg(i::text).

ืื™ืฆื˜ ืœืึธืžื™ืจ ืคึผืจื•ื‘ื™ืจืŸ ืฆื• ืœื™ื™ืขื ืขืŸ ื“ื™ ื•ื•ืขืจื˜ ืคื•ืŸ ื™ืขื“ืขืจ ืฉืœื™ืกืœ 8 ืžืืœ - ื ืึธืš ืึทืœืข, ืื•ื™ื‘ ืื™ืจ ื˜ืึธืŸ ื ื™ื˜ ืึทืงืกืขืก ื“ืขื ื•ื•ืขืจื˜ืขืจื‘ื•ืš, ื•ื•ืึธืก ืื™ื– ืขืก ื“ืืจืฃ?

ืืคืฉืืฆื•ื ื’ ืฉืจื™ืคื˜: ืœื™ื™ืขื ืขืŸ ืคื•ืŸ ืึท ื•ื•ืขืจื˜ืขืจื‘ื•ืš

WITH T AS (
  SELECT
    *
  , (
      SELECT
        regexp_replace(ea[array_length(ea, 1)], '^Execution Time: (d+.d+) ms$', '1')::real et
      FROM
        (
          SELECT
            array_agg(el) ea
          FROM
            dblink('port= ' || current_setting('port') || ' dbname=' || current_database(), $$
              explain analyze
              WITH dict AS (
                SELECT
                  json_object(
                    array_agg(i::text)
                  , array_agg(i::text)
                  )
                FROM
                  generate_series(1, $$ || (1 << v) || $$) i
              )
              SELECT
                (TABLE dict) -> (i % ($$ || (1 << v) || $$) + 1)::text
              FROM
                generate_series(1, $$ || (1 << (v + 3)) || $$) i
            $$) T(el text)
        ) T
    ) et
  FROM
    generate_series(0, 19) v
  , LATERAL generate_series(1, 7) i
  ORDER BY
    1, 2
)
SELECT
  v
, avg(et)::numeric(32,3)
FROM
  T
GROUP BY
  1
ORDER BY
  1;

PostgreSQL Antipatterns: ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืฉืœืึธื’ืŸ ืฉื•ื•ืขืจ JOIN

ืื•ืŸ... ืฉื•ื™ืŸ ื‘ืขืจืš ืžื™ื˜ 2 ^ 6 ืงื™ื–, ืœื™ื™ืขื ืขืŸ ืคื•ืŸ ืึท ื“ื–ืฉืกืึธืŸ ื•ื•ืขืจื˜ืขืจื‘ื•ืš ื”ื™ื™ื‘ื˜ ืฆื• ืคืึทืจืœื™ืจืŸ ืขื˜ืœืขื›ืข ืžืึธืœ ืœื™ื™ืขื ืขืŸ ืคึฟื•ืŸ hstore, ืคึฟืึทืจ jsonb ื“ืขืจ ื–ืขืœื‘ื™ืงืขืจ ื›ืึทืคึผืึทื ื– ื‘ื™ื™ 2 ^ 9.

ืœืขืฆื˜ืข ืžืกืงื ื•ืช:

  • ืื•ื™ื‘ ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื˜ืึธืŸ ืขืก ืคืึทืจื‘ื™ื ื“ืŸ ืžื™ื˜ ืงื™ื™ืคืœ ืจื™ืคึผื™ื˜ื™ื ื’ ืจืขืงืึธืจื“ืก - ืขืก ืื™ื– ื‘ืขืกืขืจ ืฆื• ื ื•ืฆืŸ "ื•ื•ืขืจื˜ืขืจื‘ื•ืš" ืคื•ืŸ ื“ื™ ื˜ื™ืฉ
  • ืื•ื™ื‘ ื“ื™ื™ืŸ ื•ื•ืขืจื˜ืขืจื‘ื•ืš ืื™ื– ื“ืขืจื•ื•ืึทืจื˜ ืงืœื™ื™ืŸ ืื•ืŸ ืื™ืจ ื•ื•ืขื˜ ื ื™ืฉื˜ ืœื™ื™ืขื ืขืŸ ืคื™ืœ ืคื•ืŸ ืื™ื - ืื™ืจ ืงืขื ืขืŸ ื ื•ืฆืŸ json[b]
  • ืื™ืŸ ืืœืข ืื ื“ืขืจืข ืคืืœืŸ hstore + array_agg (ืื™ืš:: ื˜ืขืงืกื˜) ื•ื•ืขื˜ ื–ื™ื™ืŸ ืžืขืจ ืขืคืขืงื˜ื™ื•ื•

ืžืงื•ืจ: www.habr.com

ืœื™ื™ื’ืŸ ืึท ื‘ืึทืžืขืจืงื•ื ื’