PostgreSQL Antipatterns: Dictionary Hit Heavy JOIN

ืื ื• ืžืžืฉื™ื›ื™ื ืืช ืกื“ืจืช ื”ืžืืžืจื™ื ื”ืžื•ืงื“ืฉื™ื ืœืžื—ืงืจ ืฉืœ ื“ืจื›ื™ื ืœื ื™ื“ื•ืขื•ืช ืœืฉื™ืคื•ืจ ื”ื‘ื™ืฆื•ืขื™ื ืฉืœ ืฉืื™ืœืชื•ืช PostgreSQL "ืคืฉื•ื˜ื•ืช ืœื›ืื•ืจื”":

ืืœ ืชื—ืฉื•ื‘ ืฉืื ื™ ืœื ื›ืœ ื›ืš ืื•ื”ื‘ JOIN... :)

ืื‘ืœ ืœืขืชื™ื ืงืจื•ื‘ื•ืช ื‘ืœืขื“ื™ื•, ื”ื‘ืงืฉื” ืžืชื‘ืจืจืช ื›ืคืจื•ื“ื•ืงื˜ื™ื‘ื™ืช ืžืฉืžืขื•ืชื™ืช ืžืืฉืจ ืื™ืชื”. ืื– ื”ื™ื•ื ื ื ืกื” ืœื”ื™ืคื˜ืจ ืž-JOIN ืขืชื™ืจ ืžืฉืื‘ื™ื - ืฉื™ืžื•ืฉ ื‘ืžื™ืœื•ืŸ.

PostgreSQL Antipatterns: Dictionary Hit Heavy 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: Dictionary Hit Heavy JOIN
[ื”ืกืชื›ืœ ื‘-explain.tensor.ru]

ืžืชื‘ืจืจ ื›ื™ 1/3 ื–ืžืŸ ื›ื•ืœืœ ื•-3/4 ืงืจื™ืื•ืช ื“ืคื™ ื ืชื•ื ื™ื ื ืขืฉื• ืจืง ื›ื“ื™ ืœื—ืคืฉ ืืช ื”ืžื—ื‘ืจ 100 ืคืขืžื™ื - ืขื‘ื•ืจ ื›ืœ ืžืฉื™ืžืช ืคืœื˜. ืื‘ืœ ืื ื—ื ื• ื™ื•ื“ืขื™ื ืฉื‘ื™ืŸ ืžืื•ืช ืืœื” ืจืง 20 ืฉื•ื ื™ื - ื”ืื ื ื™ืชืŸ ืœื”ืฉืชืžืฉ ื‘ื™ื“ืข ื”ื–ื”?

hstore-ืžื™ืœื•ืŸ

ื‘ื•ืื• ื ืฉืชืžืฉ ืกื•ื’ hstore ื›ื“ื™ ืœื™ืฆื•ืจ ืขืจืš ืžืคืชื— "ืžื™ืœื•ืŸ":

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: Dictionary Hit Heavy 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]serialization ื’ื‘ื•ื”ื•ืช ืœืžื“ื™ืœื›ืŸ, ืกื‘ื™ืจ ืœื”ืฉืชืžืฉ ื‘ืฉื™ื˜ื” ื–ื• ืจืง ื‘ืžืงืจื™ื ืžืกื•ื™ืžื™ื ื›ืืฉืจ ืกืจื™ืงืช ื”-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: Dictionary Hit Heavy JOIN

ื‘-PostgreSQL 11, ืขื“ ื’ื•ื“ืœ ืžื™ืœื•ืŸ ื‘ืขืจืš ืฉืœ 2^12 ืžืคืชื—ื•ืช ื”ืกื™ื“ืจื” ืœ-json ืœื•ืงื—ืช ืคื—ื•ืช ื–ืžืŸ. ื‘ืžืงืจื” ื–ื”, ื”ื™ืขื™ืœ ื‘ื™ื•ืชืจ ื”ื•ื ื”ืฉื™ืœื•ื‘ ืฉืœ 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: Dictionary Hit Heavy JOIN

ื•... ื›ื‘ืจ ื‘ืขืจืš ืขื ืžืงืฉื™ื 2^6, ื”ืงืจื™ืื” ืžืžื™ืœื•ืŸ json ืžืชื—ื™ืœื” ืœื”ืคืกื™ื“ ืžืกืคืจ ืคืขืžื™ื ืงืจื™ืื” ืž-hstore, ืขื‘ื•ืจ jsonb ืื•ืชื• ื“ื‘ืจ ืงื•ืจื” ื‘-2^9.

ืžืกืงื ื•ืช ืกื•ืคื™ื•ืช:

  • ืื ืืชื” ืฆืจื™ืš ืœืขืฉื•ืช ืืช ื–ื” ื”ืฆื˜ืจืฃ ืขื ืžืกืคืจ ืจืฉื•ืžื•ืช ืฉื—ื•ื–ืจื•ืช ืขืœ ืขืฆืžืŸ - ืขื“ื™ืฃ ืœื”ืฉืชืžืฉ ื‘"ืžื™ืœื•ืŸ" ืฉืœ ื”ื˜ื‘ืœื”
  • ืื ื”ืžื™ืœื•ืŸ ืฉืœืš ืฆืคื•ื™ ืงื˜ืŸ ื•ืœื ืชืงืจืื• ืžืžื ื• ื”ืจื‘ื” - ืืชื” ื™ื›ื•ืœ ืœื”ืฉืชืžืฉ ื‘-json[b]
  • ื‘ื›ืœ ืฉืืจ ื”ืžืงืจื™ื hstore + array_agg(i::text) ื™ื”ื™ื” ื™ืขื™ืœ ื™ื•ืชืจ

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

ื”ื•ืกืคืช ืชื’ื•ื‘ื”