PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията

Π’ слоТни ERP систСми ΠΌΠ½ΠΎΠ³ΠΎ субСкти ΠΈΠΌΠ°Ρ‚ ΠΉΠ΅Ρ€Π°Ρ€Ρ…ΠΈΡ‡Π΅Π½ Ρ…Π°Ρ€Π°ΠΊΡ‚Π΅Ρ€ΠΊΠΎΠ³Π°Ρ‚ΠΎ Ρ…ΠΎΠΌΠΎΠ³Π΅Π½Π½ΠΈ ΠΎΠ±Π΅ΠΊΡ‚ΠΈ сС подрСдят Π² Π΄ΡŠΡ€Π²ΠΎ Π½Π° Π²Π·Π°ΠΈΠΌΠΎΠΎΡ‚Π½ΠΎΡˆΠ΅Π½ΠΈΡΡ‚Π° ΠΏΡ€Π΅Π΄Ρ†ΠΈ-ΠΏΠΎΡ‚ΠΎΠΌΡ†ΠΈ - Ρ‚ΠΎΠ²Π° Π΅ ΠΎΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΠΎΠ½Π½Π°Ρ‚Π° структура Π½Π° прСдприятиСто (всички Ρ‚Π΅Π·ΠΈ ΠΊΠ»ΠΎΠ½ΠΎΠ²Π΅, ΠΎΡ‚Π΄Π΅Π»ΠΈ ΠΈ Ρ€Π°Π±ΠΎΡ‚Π½ΠΈ Π³Ρ€ΡƒΠΏΠΈ), ΠΈ ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³ΡŠΡ‚ Π½Π° стокитС, ΠΈ областитС Π½Π° Ρ€Π°Π±ΠΎΡ‚Π°, ΠΈ гСографията Π½Π° Ρ‚ΠΎΡ‡ΠΊΠΈΡ‚Π΅ Π·Π° ΠΏΡ€ΠΎΠ΄Π°ΠΆΠ±Π°,...

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията

Π’ΡΡŠΡ‰Π½ΠΎΡΡ‚ няма Ρ‚Π°ΠΊΡŠΠ² области Π·Π° автоматизация Π½Π° бизнСса, ΠΊΡŠΠ΄Π΅Ρ‚ΠΎ Π² Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ няма Π΄Π° ΠΈΠΌΠ° йСрархия. Но Π΄ΠΎΡ€ΠΈ ΠΈ Π΄Π° Π½Π΅ Ρ€Π°Π±ΠΎΡ‚ΠΈΡ‚Π΅ β€žΠ·Π° Π±ΠΈΠ·Π½Π΅ΡΠ°β€œ, ΠΏΠ°ΠΊ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ лСсно Π΄Π° сС Π½Π°Ρ‚ΡŠΠΊΠ½Π΅Ρ‚Π΅ Π½Π° ΠΉΠ΅Ρ€Π°Ρ€Ρ…ΠΈΡ‡Π½ΠΈ Π²Π·Π°ΠΈΠΌΠΎΠΎΡ‚Π½ΠΎΡˆΠ΅Π½ΠΈΡ. Π‘Π°Π½Π°Π»Π½ΠΎ Π΅, Π΄ΠΎΡ€ΠΈ Π²Π°ΡˆΠ΅Ρ‚ΠΎ родословно Π΄ΡŠΡ€Π²ΠΎ ΠΈΠ»ΠΈ Π΅Ρ‚Π°ΠΆΠ΅Π½ ΠΏΠ»Π°Π½ Π½Π° помСщСния Π² Ρ‚ΡŠΡ€Π³ΠΎΠ²ΡΠΊΠΈ Ρ†Π΅Π½Ρ‚ΡŠΡ€ Π΅ Π΅Π΄Π½Π° ΠΈ ΡΡŠΡ‰Π° структура.

Има ΠΌΠ½ΠΎΠ³ΠΎ Π½Π°Ρ‡ΠΈΠ½ΠΈ Π·Π° ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π°Π½Π΅ Π½Π° Ρ‚Π°ΠΊΠΎΠ²Π° Π΄ΡŠΡ€Π²ΠΎ Π² Π‘Π£Π‘Π”, Π½ΠΎ днСс Ρ‰Π΅ сС ΡΡŠΡΡ€Π΅Π΄ΠΎΡ‚ΠΎΡ‡ΠΈΠΌ само Π²ΡŠΡ€Ρ…Ρƒ Π΅Π΄ΠΈΠ½ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚:

CREATE TABLE hier(
  id
    integer
      PRIMARY KEY
, pid
    integer
      REFERENCES hier
, data
    json
);

CREATE INDEX ON hier(pid); -- Π½Π΅ Π·Π°Π±Ρ‹Π²Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ FK Π½Π΅ ΠΏΠΎΠ΄Ρ€Π°Π·ΡƒΠΌΠ΅Π²Π°Π΅Ρ‚ автосозданиС индСкса, Π² ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠ΅ ΠΎΡ‚ PK

И Π΄ΠΎΠΊΠ°Ρ‚ΠΎ Π½Π°Π΄Π½ΠΈΡ‡Π°Ρ‚Π΅ Π² Π΄ΡŠΠ»Π±ΠΈΠ½ΠΈΡ‚Π΅ Π½Π° йСрархията, тя Ρ‚ΡŠΡ€ΠΏΠ΅Π»ΠΈΠ²ΠΎ Ρ‡Π°ΠΊΠ° Π΄Π° Π²ΠΈΠ΄ΠΈ ΠΊΠΎΠ»ΠΊΠΎ [Π½Π΅]Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΈ Ρ‰Π΅ Π±ΡŠΠ΄Π°Ρ‚ Π²Π°ΡˆΠΈΡ‚Π΅ β€žΠ½Π°ΠΈΠ²Π½ΠΈβ€ Π½Π°Ρ‡ΠΈΠ½ΠΈ Π½Π° Ρ€Π°Π±ΠΎΡ‚Π° с Ρ‚Π°ΠΊΠ°Π²Π° структура.

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията
НСка Π΄Π° Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ Ρ‚ΠΈΠΏΠΈΡ‡Π½ΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ Π²ΡŠΠ·Π½ΠΈΠΊΠ²Π°Ρ‚, тяхната рСализация Π² SQL ΠΈ Π΄Π° сС ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ Π΄Π° ΠΏΠΎΠ΄ΠΎΠ±Ρ€ΠΈΠΌ тяхната производитСлност.

#1. Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°?

НСка със сигурност ΠΏΡ€ΠΈΠ΅ΠΌΠ΅ΠΌ, Ρ‡Π΅ Ρ‚Π°Π·ΠΈ структура Ρ‰Π΅ отразява подчинСността Π½Π° ΠΎΡ‚Π΄Π΅Π»ΠΈΡ‚Π΅ Π² структурата Π½Π° организацията: ΠΎΡ‚Π΄Π΅Π»ΠΈ, ΠΎΡ‚Π΄Π΅Π»ΠΈ, сСктори, ΠΊΠ»ΠΎΠ½ΠΎΠ²Π΅, Ρ€Π°Π±ΠΎΡ‚Π½ΠΈ Π³Ρ€ΡƒΠΏΠΈ... - ΠΊΠ°ΠΊΡ‚ΠΎ ΠΈ Π΄Π° Π³ΠΈ Π½Π°Ρ€ΠΈΡ‡Π°Ρ‚Π΅.
PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията

ΠŸΡŠΡ€Π²ΠΎ, Π½Π΅ΠΊΠ° Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π°ΠΌΠ΅ Π½Π°ΡˆΠ΅Ρ‚ΠΎ β€žΠ΄ΡŠΡ€Π²ΠΎβ€œ ΠΎΡ‚ 10K Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚Π°

INSERT INTO hier
WITH RECURSIVE T AS (
  SELECT
    1::integer id
  , '{1}'::integer[] pids
UNION ALL
  SELECT
    id + 1
  , pids[1:(random() * array_length(pids, 1))::integer] || (id + 1)
  FROM
    T
  WHERE
    id < 10000
)
SELECT
  pids[array_length(pids, 1)] id
, pids[array_length(pids, 1) - 1] pid
FROM
  T;

НСка Π·Π°ΠΏΠΎΡ‡Π½Π΅ΠΌ с Π½Π°ΠΉ-простата Π·Π°Π΄Π°Ρ‡Π° - Π½Π°ΠΌΠΈΡ€Π°Π½Π΅ Π½Π° всички слуТитСли, ΠΊΠΎΠΈΡ‚ΠΎ работят Π² ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ сСктор ΠΈΠ»ΠΈ ΠΎΡ‚ Π³Π»Π΅Π΄Π½Π° Ρ‚ΠΎΡ‡ΠΊΠ° Π½Π° йСрархията - Π½Π°ΠΌΠ΅Ρ€Π΅Ρ‚Π΅ всички Π΄Π΅Ρ†Π° Π½Π° възСл. Π‘ΡŠΡ‰ΠΎ Ρ‚Π°ΠΊΠ° Π±ΠΈ Π±ΠΈΠ»ΠΎ Ρ…ΡƒΠ±Π°Π²ΠΎ Π΄Π° сС ΠΏΠΎΠ»ΡƒΡ‡ΠΈ β€žΠ΄ΡŠΠ»Π±ΠΎΡ‡ΠΈΠ½Π°Ρ‚Π°β€œ Π½Π° ΠΏΠΎΡ‚ΠΎΠΌΡŠΠΊΠ°... Всичко Ρ‚ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅ Π΄Π° Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π·Π° ΠΈΠ·Π³Ρ€Π°ΠΆΠ΄Π°Π½Π΅ Π½Π° някакъв Π²ΠΈΠ΄ комплСксСн ΠΏΠΎΠ΄Π±ΠΎΡ€ въз основа Π½Π° списъка с ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΎΠ½Π½ΠΈ Π½ΠΎΠΌΠ΅Ρ€Π° Π½Π° Ρ‚Π΅Π·ΠΈ слуТитСли.

Всичко Π±ΠΈ Π±ΠΈΠ»ΠΎ Π½Π°Ρ€Π΅Π΄, Π°ΠΊΠΎ ΠΈΠΌΠ° само няколко Π½ΠΈΠ²Π° Π½Π° Ρ‚Π΅Π·ΠΈ наслСдници ΠΈ броят ΠΈΠΌ Π΅ Π² Ρ€Π°ΠΌΠΊΠΈΡ‚Π΅ Π½Π° Π΄ΡƒΠ·ΠΈΠ½Π°, Π½ΠΎ Π°ΠΊΠΎ ΠΈΠΌΠ° ΠΏΠΎΠ²Π΅Ρ‡Π΅ ΠΎΡ‚ 5 Π½ΠΈΠ²Π° ΠΈ Π²Π΅Ρ‡Π΅ ΠΈΠΌΠ° дСсСтки ΠΏΠΎΡ‚ΠΎΠΌΡ†ΠΈ, ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΈΠΌΠ° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ. НСка Π΄Π° Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΠΊΠ°ΠΊ са написани (ΠΈ работят) Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½ΠΈΡ‚Π΅ ΠΎΠΏΡ†ΠΈΠΈ Π·Π° Ρ‚ΡŠΡ€ΡΠ΅Π½Π΅ Π½Π°Π΄ΠΎΠ»Ρƒ ΠΏΠΎ Π΄ΡŠΡ€Π²ΠΎΡ‚ΠΎ. Но ΠΏΡŠΡ€Π²ΠΎ, Π½Π΅ΠΊΠ° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ ΠΊΠΎΠΈ възли Ρ‰Π΅ Π±ΡŠΠ΄Π°Ρ‚ Π½Π°ΠΉ-интСрСсни Π·Π° Π½Π°ΡˆΠ΅Ρ‚ΠΎ изслСдванС.

Най-ΠΌΠ½ΠΎΠ³ΠΎ "Π”ΡŠΠ»Π±ΠΎΠΊ" ΠΏΠΎΠ΄Π΄ΡŠΡ€Π²Π΅Ρ‚Π°:

WITH RECURSIVE T AS (
  SELECT
    id
  , pid
  , ARRAY[id] path
  FROM
    hier
  WHERE
    pid IS NULL
UNION ALL
  SELECT
    hier.id
  , hier.pid
  , T.path || hier.id
  FROM
    T
  JOIN
    hier
      ON hier.pid = T.id
)
TABLE T ORDER BY array_length(path, 1) DESC;

 id  | pid  | path
---------------------------------------------
7624 | 7623 | {7615,7620,7621,7622,7623,7624}
4995 | 4994 | {4983,4985,4988,4993,4994,4995}
4991 | 4990 | {4983,4985,4988,4989,4990,4991}
...

Най-ΠΌΠ½ΠΎΠ³ΠΎ "ΡˆΠΈΡ€ΠΎΠΊ" ΠΏΠΎΠ΄Π΄ΡŠΡ€Π²Π΅Ρ‚Π°:

...
SELECT
  path[1] id
, count(*)
FROM
  T
GROUP BY
  1
ORDER BY
  2 DESC;

id   | count
------------
5300 |   30
 450 |   28
1239 |   27
1573 |   25

Π—Π° Ρ‚Π΅Π·ΠΈ заявки ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ…ΠΌΠ΅ типичния рСкурсивно JOIN:
PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията

ΠžΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎ, с Ρ‚ΠΎΠ·ΠΈ ΠΌΠΎΠ΄Π΅Π» Π½Π° заявка броят Π½Π° ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠΈΡ‚Π΅ Ρ‰Π΅ ΡΡŠΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²Π° Π½Π° общия Π±Ρ€ΠΎΠΉ ΠΏΠΎΡ‚ΠΎΠΌΡ†ΠΈ (ΠΈ ΠΈΠΌΠ° няколко дСсСтки ΠΎΡ‚ тях) ΠΈ Ρ‚ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΎΡ‚Π½Π΅ΠΌΠ΅ доста Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»Π½ΠΈ рСсурси ΠΈ Π² Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ Π½Π° Ρ‚ΠΎΠ²Π° Π²Ρ€Π΅ΠΌΠ΅.

НСка ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ β€žΠ½Π°ΠΉ-ΡˆΠΈΡ€ΠΎΠΊΠΎΡ‚ΠΎβ€œ ΠΏΠΎΠ΄Π΄ΡŠΡ€Π²ΠΎ:

WITH RECURSIVE T AS (
  SELECT
    id
  FROM
    hier
  WHERE
    id = 5300
UNION ALL
  SELECT
    hier.id
  FROM
    T
  JOIN
    hier
      ON hier.pid = T.id
)
TABLE T;

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията
[Π²ΠΈΠΆΡ‚Π΅ expand.tensor.ru]

ΠšΠ°ΠΊΡ‚ΠΎ сС ΠΎΡ‡Π°ΠΊΠ²Π°ΡˆΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всичкитС 30 записа. Но Ρ‚Π΅ ΠΎΡ‚Π΄Π΅Π»ΠΈΡ…Π° 60% ΠΎΡ‚ ΠΎΠ±Ρ‰ΠΎΡ‚ΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π·Π° Ρ‚ΠΎΠ²Π° - Π·Π°Ρ‰ΠΎΡ‚ΠΎ ΡΡŠΡ‰ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…Π° 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ Π² индСкса. Π’ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎ Π»ΠΈ Π΅ Π΄Π° сС Π½Π°ΠΏΡ€Π°Π²ΠΈ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

Масова корСкция ΠΏΠΎ индСкс

Врябва Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΎΡ‚Π΄Π΅Π»Π½Π° заявка Π·Π° индСкс Π·Π° всСки възСл? Оказва сС, Ρ‡Π΅ Π½Π΅ - ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° Ρ‡Π΅Ρ‚Π΅ΠΌ ΠΎΡ‚ индСкса ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° няколко клавиша навСднъТ Π² Π΅Π΄Π½ΠΎ ΠΏΠΎΠ²ΠΈΠΊΠ²Π°Π½Π΅ ΠΏΡ€Π΅Π· = ANY(array).

И във всяка Ρ‚Π°ΠΊΠ°Π²Π° Π³Ρ€ΡƒΠΏΠ° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈ ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° Π²Π·Π΅ΠΌΠ΅ΠΌ всички ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈ, Π½Π°ΠΌΠ΅Ρ€Π΅Π½ΠΈ Π² ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½Π°Ρ‚Π° ΡΡ‚ΡŠΠΏΠΊΠ°, Ρ‡Ρ€Π΅Π· β€žΠ²ΡŠΠ·Π»ΠΈβ€œ. ВоСст Π½Π° всяка слСдваща ΡΡ‚ΡŠΠΏΠΊΠ° Ρ‰Π΅ Ρ‚ΡŠΡ€ΡΠ΅Π½Π΅ Π½Π° всички ΠΏΠΎΡ‚ΠΎΠΌΡ†ΠΈ Π½Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΎ Π½ΠΈΠ²ΠΎ навСднъТ.

Π‘Π°ΠΌΠΎ Ρ‡Π΅ Ρ‚ΡƒΠΊ Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡŠΡ‚, ΠΏΡ€ΠΈ рСкурсивна сСлСкция Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚Π΅ Π΄ΠΎΡΡ‚ΡŠΠΏ Π΄ΠΎ сСбС си във Π²Π»ΠΎΠΆΠ΅Π½Π° заявка, Π½ΠΎ трябва ΠΏΠΎ някакъв Π½Π°Ρ‡ΠΈΠ½ Π΄Π° сСлСктирамС само Ρ‚ΠΎΠ²Π°, ΠΊΠΎΠ΅Ρ‚ΠΎ Π΅ Π½Π°ΠΌΠ΅Ρ€Π΅Π½ΠΎ Π½Π° ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½ΠΎΡ‚ΠΎ Π½ΠΈΠ²ΠΎ... Оказва сС, Ρ‡Π΅ Π΅ нСвъзмоТно Π΄Π° сС Π½Π°ΠΏΡ€Π°Π²ΠΈ Π²Π»ΠΎΠΆΠ΅Π½Π° заявка Π·Π° цялата сСлСкция, Π½ΠΎ Π·Π° Π½Π΅ΠΉΠ½ΠΎΡ‚ΠΎ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎ ΠΏΠΎΠ»Π΅ Π΅ възмоТно. И Ρ‚ΠΎΠ²Π° ΠΏΠΎΠ»Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС ΠΈ масив - ΠΊΠΎΠ΅Ρ‚ΠΎ трябва Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ ANY.

Π—Π²ΡƒΡ‡ΠΈ ΠΌΠ°Π»ΠΊΠΎ Π½Π°Π»ΡƒΠ΄Π½ΠΈΡ‡Π°Π²ΠΎ, Π½ΠΎ Π½Π° Π΄ΠΈΠ°Π³Ρ€Π°ΠΌΠ°Ρ‚Π° всичко Π΅ просто.

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията

WITH RECURSIVE T AS (
  SELECT
    ARRAY[id] id$
  FROM
    hier
  WHERE
    id = 5300
UNION ALL
  SELECT
    ARRAY(
      SELECT
        id
      FROM
        hier
      WHERE
        pid = ANY(T.id$)
    ) id$
  FROM
    T
  WHERE
    coalesce(id$, '{}') <> '{}' -- условиС Π²Ρ‹Ρ…ΠΎΠ΄Π° ΠΈΠ· Ρ†ΠΈΠΊΠ»Π° - пустой массив
)
SELECT
  unnest(id$) id
FROM
  T;

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията
[Π²ΠΈΠΆΡ‚Π΅ expand.tensor.ru]

И Ρ‚ΡƒΠΊ Π½Π°ΠΉ-Π²Π°ΠΆΠ½ΠΎΡ‚ΠΎ Π½Π΅ Π΅ Π΄ΠΎΡ€ΠΈ ΠΏΠΎΠ±Π΅Π΄ΠΈ 1.5 ΠΏΡŠΡ‚ΠΈ във Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎΠΈ Ρ‡Π΅ ΠΈΠ·Π²Π°Π΄ΠΈΡ…ΠΌΠ΅ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ Π±ΡƒΡ„Π΅Ρ€ΠΈ, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ ΠΈΠΌΠ°ΠΌΠ΅ само 5 извиквания към индСкса вмСсто 30!

Π”ΠΎΠΏΡŠΠ»Π½ΠΈΡ‚Π΅Π»Π΅Π½ бонус Π΅ Ρ„Π°ΠΊΡ‚ΡŠΡ‚, Ρ‡Π΅ слСд ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»Π½ΠΎΡ‚ΠΎ unnest, ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈΡ‚Π΅ Ρ‰Π΅ останат ΠΏΠΎΠ΄Ρ€Π΅Π΄Π΅Π½ΠΈ ΠΏΠΎ β€žΠ½ΠΈΠ²Π°β€œ.

Π—Π½Π°ΠΊ Π·Π° възСл

Π‘Π»Π΅Π΄Π²Π°Ρ‰ΠΎΡ‚ΠΎ ΡΡŠΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅, ΠΊΠΎΠ΅Ρ‚ΠΎ Ρ‰Π΅ ΠΏΠΎΠΌΠΎΠ³Π½Π΅ Π·Π° подобряванС Π½Π° производитСлността, Π΅ βˆ’ "листа" Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΈΠΌΠ° Π΄Π΅Ρ†Π°, тоСст Π·Π° тях ΠΈΠ·ΠΎΠ±Ρ‰ΠΎ няма Π½ΡƒΠΆΠ΄Π° Π΄Π° Π³Π»Π΅Π΄Π°Ρ‚ β€žΠ½Π°Π΄ΠΎΠ»Ρƒβ€œ. ΠŸΡ€ΠΈ Ρ„ΠΎΡ€ΠΌΡƒΠ»ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ Π½Π° Π½Π°ΡˆΠ°Ρ‚Π° Π·Π°Π΄Π°Ρ‡Π° Ρ‚ΠΎΠ²Π° ΠΎΠ·Π½Π°Ρ‡Π°Π²Π°, Ρ‡Π΅ Π°ΠΊΠΎ смС слСдвали Π²Π΅Ρ€ΠΈΠ³Π°Ρ‚Π° ΠΎΡ‚ ΠΎΡ‚Π΄Π΅Π»ΠΈ ΠΈ смС стигнали Π΄ΠΎ слуТитСл, Ρ‚ΠΎΠ³Π°Π²Π° няма Π½ΡƒΠΆΠ΄Π° Π΄Π° Ρ‚ΡŠΡ€ΡΠΈΠΌ ΠΏΠΎ-Π½Π°Ρ‚Π°Ρ‚ΡŠΠΊ ΠΏΠΎ Ρ‚ΠΎΠ·ΠΈ ΠΊΠ»ΠΎΠ½.

Π”Π° Π²Π»Π΅Π·Π΅ΠΌ Π² Π½Π°ΡˆΠ°Ρ‚Π° маса Π΄ΠΎΠΏΡŠΠ»Π½ΠΈΡ‚Π΅Π»Π΅Π½ boolean- ΠΏΠΎΠ»Π΅, ΠΊΠΎΠ΅Ρ‚ΠΎ Π²Π΅Π΄Π½Π°Π³Π° Ρ‰Π΅ Π½ΠΈ ΠΊΠ°ΠΆΠ΅ Π΄Π°Π»ΠΈ Ρ‚ΠΎΠ·ΠΈ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π΅Π½ запис Π² Π½Π°ΡˆΠ΅Ρ‚ΠΎ Π΄ΡŠΡ€Π²ΠΎ Π΅ β€žΠ²ΡŠΠ·Π΅Π»β€œ – тоСст Π΄Π°Π»ΠΈ ΠΈΠ·ΠΎΠ±Ρ‰ΠΎ ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΈΠΌΠ° наслСдници.

ALTER TABLE hier
  ADD COLUMN branch boolean;

UPDATE
  hier T
SET
  branch = TRUE
WHERE
  EXISTS(
    SELECT
      NULL
    FROM
      hier
    WHERE
      pid = T.id
    LIMIT 1
);
-- Запрос ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½: 3033 строк ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΎ Π·Π° 42 мс.

Π‘Ρ‚Ρ€Π°Ρ…ΠΎΡ‚Π΅Π½! Оказва сС, Ρ‡Π΅ само ΠΌΠ°Π»ΠΊΠΎ ΠΏΠΎΠ²Π΅Ρ‡Π΅ ΠΎΡ‚ 30% ΠΎΡ‚ всички Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚ΠΈ Π½Π° Π΄ΡŠΡ€Π²ΠΎΡ‚ΠΎ ΠΈΠΌΠ°Ρ‚ ΠΏΠΎΡ‚ΠΎΠΌΡ†ΠΈ.

Π‘Π΅Π³Π° Π½Π΅ΠΊΠ° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ ΠΌΠ°Π»ΠΊΠΎ ΠΏΠΎ-Ρ€Π°Π·Π»ΠΈΡ‡Π½Π° ΠΌΠ΅Ρ…Π°Π½ΠΈΠΊΠ° - Π²Ρ€ΡŠΠ·ΠΊΠΈ към рСкурсивната част Ρ‡Ρ€Π΅Π· LATERAL, ΠΊΠΎΠ΅Ρ‚ΠΎ Ρ‰Π΅ Π½ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈ Π½Π΅Π·Π°Π±Π°Π²Π΅Π½ Π΄ΠΎΡΡ‚ΡŠΠΏ Π΄ΠΎ ΠΏΠΎΠ»Π΅Ρ‚Π°Ρ‚Π° Π½Π° рСкурсивната β€žΡ‚Π°Π±Π»ΠΈΡ†Π°β€œ ΠΈ Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Π°Π³Ρ€Π΅Π³Π°Ρ‚Π½Π° функция с условиС Π·Π° Ρ„ΠΈΠ»Ρ‚Ρ€ΠΈΡ€Π°Π½Π΅, Π±Π°Π·ΠΈΡ€Π°Π½ΠΎ Π½Π° възСл, Π·Π° Π΄Π° Π½Π°ΠΌΠ°Π»ΠΈΠΌ Π½Π°Π±ΠΎΡ€Π° ΠΎΡ‚ ΠΊΠ»ΡŽΡ‡ΠΎΠ²Π΅:

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията

WITH RECURSIVE T AS (
  SELECT
    array_agg(id) id$
  , array_agg(id) FILTER(WHERE branch) ns$
  FROM
    hier
  WHERE
    id = 5300
UNION ALL
  SELECT
    X.*
  FROM
    T
  JOIN LATERAL (
    SELECT
      array_agg(id) id$
    , array_agg(id) FILTER(WHERE branch) ns$
    FROM
      hier
    WHERE
      pid = ANY(T.ns$)
  ) X
    ON coalesce(T.ns$, '{}') <> '{}'
)
SELECT
  unnest(id$) id
FROM
  T;

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията
[Π²ΠΈΠΆΡ‚Π΅ expand.tensor.ru]

УспяхмС Π΄Π° Π½Π°ΠΌΠ°Π»ΠΈΠΌ ΠΎΡ‰Π΅ Π΅Π΄Π½ΠΎ индСксно ΠΎΠ±Π°ΠΆΠ΄Π°Π½Π΅ ΠΈ спСчСли ΠΏΠΎΠ²Π΅Ρ‡Π΅ ΠΎΡ‚ 2 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ ΠΎΠ±Π΅ΠΌ ΠΊΠΎΡ€ΠΈΠ³ΠΈΡ€Π°Π½.

#2. Π”Π° сС β€‹β€‹Π²ΡŠΡ€Π½Π΅ΠΌ към ΠΊΠΎΡ€Π΅Π½ΠΈΡ‚Π΅

Π’ΠΎΠ·ΠΈ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΡŠΠΌ Ρ‰Π΅ бъдС ΠΏΠΎΠ»Π΅Π·Π΅Π½, Π°ΠΊΠΎ трябва Π΄Π° ΡΡŠΠ±Π΅Ρ€Π΅Ρ‚Π΅ записи Π·Π° всички Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚ΠΈ "Π½Π°Π³ΠΎΡ€Π΅ Π² Π΄ΡŠΡ€Π²ΠΎΡ‚ΠΎ", ΠΊΠ°Ρ‚ΠΎ ΡΡŠΡ‰Π΅Π²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ Π·Π°ΠΏΠ°Π·ΠΈΡ‚Π΅ информация Π·Π° Ρ‚ΠΎΠ²Π° ΠΊΠΎΠΉ ΠΈΠ·Ρ…ΠΎΠ΄Π΅Π½ лист (ΠΈ с ΠΊΠ°ΠΊΠ²ΠΈ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈ) Π΅ ΠΏΡ€ΠΈΡ‡ΠΈΠ½ΠΈΠ» Π²ΠΊΠ»ΡŽΡ‡Π²Π°Π½Π΅Ρ‚ΠΎ ΠΌΡƒ Π² ΠΈΠ·Π²Π°Π΄ΠΊΠ°Ρ‚Π° - Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π·Π° Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π°Π½Π΅ Π½Π° ΠΎΠ±ΠΎΠ±Ρ‰Π΅Π½ ΠΎΡ‚Ρ‡Π΅Ρ‚ с Π°Π³Ρ€Π΅Π³ΠΈΡ€Π°Π½Π΅ във възли.

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията
Π’ΠΎΠ²Π°, ΠΊΠΎΠ΅Ρ‚ΠΎ слСдва, трябва Π΄Π° сС ΠΏΡ€ΠΈΠ΅ΠΌΠ° СдинствСно ΠΊΠ°Ρ‚ΠΎ доказатСлство Π·Π° концСпцията, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ заявката сС ΠΎΠΊΠ°Π·Π²Π° ΠΌΠ½ΠΎΠ³ΠΎ Ρ‚Ρ€ΠΎΠΌΠ°Π²Π°. Но Π°ΠΊΠΎ Π΄ΠΎΠΌΠΈΠ½ΠΈΡ€Π° във Π²Π°ΡˆΠ°Ρ‚Π° Π±Π°Π·Π° Π΄Π°Π½Π½ΠΈ, трябва Π΄Π° помислитС Π·Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΈ Ρ‚Π΅Ρ…Π½ΠΈΠΊΠΈ.

НСка Π·Π°ΠΏΠΎΡ‡Π½Π΅ΠΌ с няколко прости Ρ‚Π²ΡŠΡ€Π΄Π΅Π½ΠΈΡ:

  • Π‘ΡŠΡ‰ΠΈΡΡ‚ запис ΠΎΡ‚ Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ Най-Π΄ΠΎΠ±Ρ€Π΅ Π΅ Π΄Π° Π³ΠΎ ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Ρ‚Π΅ само вСднъТ.
  • Записи ΠΎΡ‚ Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ По-Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΎ Π΅ Π΄Π° Ρ‡Π΅Ρ‚Π΅Ρ‚Π΅ Π½Π° Π³Ρ€ΡƒΠΏΠΈΠΎΡ‚ΠΊΠΎΠ»ΠΊΠΎΡ‚ΠΎ сам.

Π‘Π΅Π³Π° Π½Π΅ΠΊΠ° сС ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ Π΄Π° конструирамС заявката, ΠΎΡ‚ която сС Π½ΡƒΠΆΠ΄Π°Π΅ΠΌ.

Π‘Ρ‚ΡŠΠΏΠΊΠ° 1

ΠžΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎ, ΠΊΠΎΠ³Π°Ρ‚ΠΎ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€Π°ΠΌΠ΅ рСкурсия (къдС Π±ΠΈΡ…ΠΌΠ΅ Π±ΠΈΠ»ΠΈ Π±Π΅Π· нСя!) Ρ‰Π΅ трябва Π΄Π° ΠΈΠ·Π²Π°Π΄ΠΈΠΌ записитС Π½Π° самитС листа въз основа Π½Π° Π½Π°Π±ΠΎΡ€Π° ΠΎΡ‚ ΠΏΡŠΡ€Π²ΠΎΠ½Π°Ρ‡Π°Π»Π½ΠΈ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈ:

WITH RECURSIVE tree AS (
  SELECT
    rec -- это Ρ†Π΅Π»ΡŒΠ½Π°Ρ запись Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹
  , id::text chld -- это "Π½Π°Π±ΠΎΡ€" ΠΏΡ€ΠΈΠ²Π΅Π΄ΡˆΠΈΡ… сюда исходных Π»ΠΈΡΡ‚ΡŒΠ΅Π²
  FROM
    hier rec
  WHERE
    id = ANY('{1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192}'::integer[])
UNION ALL
  ...

Ако Π½Π° някого ΠΌΡƒ сС стори странно, Ρ‡Π΅ β€žΠ½Π°Π±ΠΎΡ€ΡŠΡ‚β€œ сС ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π° ΠΊΠ°Ρ‚ΠΎ Π½ΠΈΠ·, Π° Π½Π΅ ΠΊΠ°Ρ‚ΠΎ масив, Ρ‚ΠΎΠ³Π°Π²Π° ΠΈΠΌΠ° просто обяснСниС Π·Π° Ρ‚ΠΎΠ²Π°. Има Π²Π³Ρ€Π°Π΄Π΅Π½Π° Π°Π³Ρ€Π΅Π³ΠΈΡ€Π°Ρ‰Π° функция Π·Π° β€žΡΠ»Π΅ΠΏΠ²Π°Π½Π΅β€œ Π·Π° Π½ΠΈΠ·ΠΎΠ²Π΅ string_agg, Π½ΠΎ Π½Π΅ ΠΈ Π·Π° масиви. Π’ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‡Π΅ тя лСсно Π·Π° изпълнСниС сами.

Π‘Ρ‚ΡŠΠΏΠΊΠ° 2

Π‘Π΅Π³Π° Ρ‰Π΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ Π½Π°Π±ΠΎΡ€ ΠΎΡ‚ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈ Π½Π° сСкции, ΠΊΠΎΠΈΡ‚ΠΎ Ρ‰Π΅ трябва Π΄Π° Π±ΡŠΠ΄Π°Ρ‚ ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Π½ΠΈ Π΄ΠΎΠΏΡŠΠ»Π½ΠΈΡ‚Π΅Π»Π½ΠΎ. ΠŸΠΎΡ‡Ρ‚ΠΈ Π²ΠΈΠ½Π°Π³ΠΈ Ρ‚Π΅ Ρ‰Π΅ Π±ΡŠΠ΄Π°Ρ‚ Π΄ΡƒΠ±Π»ΠΈΡ€Π°Π½ΠΈ Π² Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ записи Π½Π° оригиналния ΠΊΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚ - Ρ‚Π°ΠΊΠ° Π±ΠΈΡ…ΠΌΠ΅ Π½Π°ΠΏΡ€Π°Π²ΠΈΠ»ΠΈ Π³Ρ€ΡƒΠΏΠΈΡ€Π°ΠΉΡ‚Π΅ Π³ΠΈ, ΠΊΠ°Ρ‚ΠΎ Π·Π°ΠΏΠ°Π·Π²Π° информацията Π·Π° листата Π½Π° ΠΈΠ·Ρ‚ΠΎΡ‡Π½ΠΈΠΊΠ°.

Но Ρ‚ΡƒΠΊ Π½ΠΈ ΠΎΡ‡Π°ΠΊΠ²Π°Ρ‚ Ρ‚Ρ€ΠΈ нСприятности:

  1. β€žΠ‘ΡƒΠ±Ρ€Π΅ΠΊΡƒΡ€ΡΠΈΠ²Π½Π°Ρ‚Π°β€œ част Π½Π° заявката Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΡŠΠ΄ΡŠΡ€ΠΆΠ° Π°Π³Ρ€Π΅Π³Π°Ρ‚Π½ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ с GROUP BY.
  2. ΠŸΡ€Π΅ΠΏΡ€Π°Ρ‚ΠΊΠ° към рСкурсивна β€žΡ‚Π°Π±Π»ΠΈΡ†Π°β€œ Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС във Π²Π»ΠΎΠΆΠ΅Π½Π° подзаявка.
  3. Заявка Π² рСкурсивната част Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΡŠΠ΄ΡŠΡ€ΠΆΠ° CTE.

Π—Π° щастиС всички Ρ‚Π΅Π·ΠΈ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ са доста лСсни Π·Π° заобикалянС. Π”Π° Π·Π°ΠΏΠΎΡ‡Π½Π΅ΠΌ ΠΎΡ‚ края.

CTE Π² рСкурсивна част

Π•Ρ‚ΠΎ Π³ΠΎ Π½Π΅ Π²ΡŠΡ€ΡˆΠΈ Ρ€Π°Π±ΠΎΡ‚Π°:

WITH RECURSIVE tree AS (
  ...
UNION ALL
  WITH T (...)
  SELECT ...
)

И Ρ‚Π°ΠΊΠ° Ρ€Π°Π±ΠΎΡ‚ΠΈ, скобитС правят Ρ€Π°Π·Π»ΠΈΠΊΠ°Ρ‚Π°!

WITH RECURSIVE tree AS (
  ...
UNION ALL
  (
    WITH T (...)
    SELECT ...
  )
)

Π’Π»ΠΎΠΆΠ΅Π½Π° заявка срСщу рСкурсивна "Ρ‚Π°Π±Π»ΠΈΡ†Π°"

Π₯ΠΌ... РСкурсивСн CTE Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС Π΄ΠΎΡΡ‚ΡŠΠΏΠ΅Π½ Π² подзаявка. Но ΠΌΠΎΠΆΠ΅ Π΄Π° Π΅ Π²ΡŠΡ‚Ρ€Π΅ Π² CTE! И Π²Π»ΠΎΠΆΠ΅Π½Π° заявка Π²Π΅Ρ‡Π΅ ΠΈΠΌΠ° Π΄ΠΎΡΡ‚ΡŠΠΏ Π΄ΠΎ Ρ‚ΠΎΠ·ΠΈ CTE!

GROUP BY Π²ΡŠΡ‚Ρ€Π΅ΡˆΠ½Π° рСкурсия

НСприятно Π΅, Π½ΠΎ... ИмамС лСсСн Π½Π°Ρ‡ΠΈΠ½ Π΄Π° Π΅ΠΌΡƒΠ»ΠΈΡ€Π°ΠΌΠ΅ GROUP BY с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° DISTINCT ON ΠΈ ΠΏΡ€ΠΎΠ·ΠΎΡ€Π΅Ρ‡Π½ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ!

SELECT
  (rec).pid id
, string_agg(chld::text, ',') chld
FROM
  tree
WHERE
  (rec).pid IS NOT NULL
GROUP BY 1 -- Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚!

И Π΅Ρ‚ΠΎ ΠΊΠ°ΠΊ Ρ€Π°Π±ΠΎΡ‚ΠΈ!

SELECT DISTINCT ON((rec).pid)
  (rec).pid id
, string_agg(chld::text, ',') OVER(PARTITION BY (rec).pid) chld
FROM
  tree
WHERE
  (rec).pid IS NOT NULL

Π‘Π΅Π³Π° Ρ€Π°Π·Π±ΠΈΡ€Π°ΠΌΠ΅ Π·Π°Ρ‰ΠΎ числовият ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ бСшС ΠΏΡ€Π΅Π²ΡŠΡ€Π½Π°Ρ‚ Π² тСкст - Π·Π° Π΄Π° ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° Π±ΡŠΠ΄Π°Ρ‚ съСдинСни Π·Π°Π΅Π΄Π½ΠΎ, Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈ със Π·Π°ΠΏΠ΅Ρ‚Π°ΠΈ!

Π‘Ρ‚ΡŠΠΏΠΊΠ° 3

Π—Π° Ρ„ΠΈΠ½Π°Π» Π½Π΅ Π½ΠΈ остана Π½ΠΈΡ‰ΠΎ:

  • Ρ‡Π΅Ρ‚Π΅ΠΌ записи Π½Π° β€žΡΠ΅ΠΊΡ†ΠΈΠΈβ€œ въз основа Π½Π° Π½Π°Π±ΠΎΡ€ ΠΎΡ‚ Π³Ρ€ΡƒΠΏΠΈΡ€Π°Π½ΠΈ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈ
  • сравнявамС ΠΈΠ·Π²Π°Π΄Π΅Π½ΠΈΡ‚Π΅ сСкции с β€žΠΊΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚ΠΈΡ‚Π΅β€œ Π½Π° ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»Π½ΠΈΡ‚Π΅ листовС
  • β€žΡ€Π°Π·ΡˆΠΈΡ€Π΅Ρ‚Π΅β€œ наборния Π½ΠΈΠ· с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° unnest(string_to_array(chld, ',')::integer[])

WITH RECURSIVE tree AS (
  SELECT
    rec
  , id::text chld
  FROM
    hier rec
  WHERE
    id = ANY('{1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192}'::integer[])
UNION ALL
  (
    WITH prnt AS (
      SELECT DISTINCT ON((rec).pid)
        (rec).pid id
      , string_agg(chld::text, ',') OVER(PARTITION BY (rec).pid) chld
      FROM
        tree
      WHERE
        (rec).pid IS NOT NULL
    )
    , nodes AS (
      SELECT
        rec
      FROM
        hier rec
      WHERE
        id = ANY(ARRAY(
          SELECT
            id
          FROM
            prnt
        ))
    )
    SELECT
      nodes.rec
    , prnt.chld
    FROM
      prnt
    JOIN
      nodes
        ON (nodes.rec).id = prnt.id
  )
)
SELECT
  unnest(string_to_array(chld, ',')::integer[]) leaf
, (rec).*
FROM
  tree;

PostgreSQL Antipatterns: Колко дълбока Π΅ Π·Π°Π΅ΡˆΠΊΠ°Ρ‚Π° Π΄ΡƒΠΏΠΊΠ°? Π΄Π° ΠΌΠΈΠ½Π΅ΠΌ ΠΏΡ€Π΅Π· йСрархията
[Π²ΠΈΠΆΡ‚Π΅ expand.tensor.ru]

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

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