PostgreSQL Antipatterns: Π½Π΅ΠΊΠ° ΡƒΠ΄Π°Ρ€ΠΈΠΌ тСТкия JOIN с Ρ€Π΅Ρ‡Π½ΠΈΠΊ

ΠŸΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ°Π²Π°ΠΌΠ΅ ΠΏΠΎΡ€Π΅Π΄ΠΈΡ†Π°Ρ‚Π° ΠΎΡ‚ статии, посвСтСни Π½Π° ΠΈΠ·ΡƒΡ‡Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΠΌΠ°Π»ΠΊΠΎ извСстни Π½Π°Ρ‡ΠΈΠ½ΠΈ Π·Π° подобряванС Π½Π° производитСлността Π½Π° β€žΠΏΡ€ΠΈΠ²ΠΈΠ΄Π½ΠΎ ΠΏΡ€ΠΎΡΡ‚ΠΈβ€œ заявки Π½Π° PostgreSQL:

НС си мислСтС, Ρ‡Π΅ Π½Π΅ харСсвам JOIN Ρ‚ΠΎΠ»ΠΊΠΎΠ²Π° ΠΌΠ½ΠΎΠ³ΠΎ... :)

Но чСсто Π±Π΅Π· Π½Π΅Π³ΠΎ заявката сС ΠΎΠΊΠ°Π·Π²Π° Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»Π½ΠΎ ΠΏΠΎ-ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΈΠ²Π½Π°, ΠΎΡ‚ΠΊΠΎΠ»ΠΊΠΎΡ‚ΠΎ с Π½Π΅Π³ΠΎ. Π’Π°ΠΊΠ° Ρ‡Π΅ днСс Ρ‰Π΅ ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ ΠΎΡ‚ΡŠΡ€Π²Π΅Ρ‚Π΅ сС ΠΎΡ‚ рСсурсоСмкия 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 с Ρ€Π΅Ρ‡Π½ΠΈΠΊ
[Π²ΠΈΠΆΡ‚Π΅ expand.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: Π½Π΅ΠΊΠ° ΡƒΠ΄Π°Ρ€ΠΈΠΌ тСТкия JOIN с Ρ€Π΅Ρ‡Π½ΠΈΠΊ
[Π²ΠΈΠΆΡ‚Π΅ expand.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. Ρ‚ΠΎΠ²Π° ΠΌΠ°ΡΠΈΠ²ΡŠΡ‚ ΠΎΡ‚ записи бСшС ΠΏΡ€Π΅Ρ€Π°Π±ΠΎΡ‚Π΅Π½ към масив ΠΎΡ‚ тСкстови Π½ΠΈΠ·ΠΎΠ²Π΅ (person[]::text[]), Π·Π° Π΄Π° Π³ΠΎ поставитС Π² Ρ€Π΅Ρ‡Π½ΠΈΠΊΠ° Π½Π° 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 ΠΎΡ‚Π½Π΅ΠΌΠ° ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ Π²Ρ€Π΅ΠΌΠ΅. Π’ Ρ‚ΠΎΠ·ΠΈ случай Π½Π°ΠΉ-Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½Π° Π΅ комбинацията ΠΎΡ‚ 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 ΠΊΠ»ΡŽΡ‡Π° Ρ‡Π΅Ρ‚Π΅Π½Π΅Ρ‚ΠΎ ΠΎΡ‚ json Ρ€Π΅Ρ‡Π½ΠΈΠΊ Π·Π°ΠΏΠΎΡ‡Π²Π° Π΄Π° сС Π³ΡƒΠ±ΠΈ ΠΌΠ½ΠΎΠ³ΠΎΠΊΡ€Π°Ρ‚Π½ΠΎ Ρ‡Π΅Ρ‚Π΅Π½Π΅ ΠΎΡ‚ hstore, Π·Π° jsonb ΡΡŠΡ‰ΠΎΡ‚ΠΎ сС случва ΠΏΡ€ΠΈ 2^9.

ΠžΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»Π½ΠΈ Π·Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ:

  • Π°ΠΊΠΎ трябва Π΄Π° Π³ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ‚Π΅ ПРИБΠͺΠ•Π”Π˜ΠΠ•Π’Π• сС с мноТСство повтарящи сС записи β€” ΠΏΠΎ-Π΄ΠΎΠ±Ρ€Π΅ Π΅ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ β€žΡ€Π΅Ρ‡Π½ΠΈΠΊβ€œ Π½Π° Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π°
  • Π°ΠΊΠΎ сС ΠΎΡ‡Π°ΠΊΠ²Π° Π²Π°ΡˆΠΈΡΡ‚ Ρ€Π΅Ρ‡Π½ΠΈΠΊ малък ΠΈ няма Π΄Π° ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Ρ‚Π΅ ΠΌΠ½ΠΎΠ³ΠΎ ΠΎΡ‚ Π½Π΅Π³ΠΎ - ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ json[b]
  • във всички останали случаи hstore + array_agg(i::тСкст) Ρ‰Π΅ бъдС ΠΏΠΎ-Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π΅Π½

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

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