PostgreSQL AntipatternsαŸ– β€œInfinity is not the limit!”, or a little about recursion

αžŠαŸ†αžŽαžΎαžšαž‘αžΎαž„αžœαž·αž‰ - αž™αž“αŸ’αžαž€αžΆαžšαžŠαŸαž˜αžΆαž“αž₯αž‘αŸ’αž’αž·αž–αž› αž“αž·αž„αž„αžΆαž™αžŸαŸ’αžšαž½αž› αž”αŸ’αžšαžŸαž·αž“αž”αžΎαžŸαž€αž˜αŸ’αž˜αž—αžΆαž– "αžŸαŸŠαžΈαž‡αž˜αŸ’αžšαŸ…" αžŠαžΌαž…αž‚αŸ’αž“αžΆαžαŸ’αžšαžΌαžœαž”αžΆαž“αž’αž“αž»αžœαžαŸ’αžαž›αžΎαž‘αž·αž“αŸ’αž“αž“αŸαž™αžŠαŸ‚αž›αž–αžΆαž€αŸ‹αž–αŸαž“αŸ’αž’αŸ” αž”αŸ‰αž»αž“αŸ’αžαŸ‚β€‹αž€αžΆαžšβ€‹αž€αžΎαžαž‘αžΎαž„β€‹αžœαž·αž‰β€‹αžŠαŸ„αž™β€‹αž˜αž·αž“β€‹αž’αžΆαž…β€‹αž‚αŸ’αžšαž”αŸ‹αž‚αŸ’αžšαž„β€‹αž”αžΆαž“β€‹αž‚αžΊβ€‹αž‡αžΆβ€‹αž’αŸ†αž–αžΎβ€‹αž’αžΆαž€αŸ’αžšαž€αŸ‹β€‹αž˜αž½αž™β€‹αžŠαŸ‚αž›β€‹αž’αžΆαž…β€‹αž“αžΆαŸ†β€‹αž±αŸ’αž™β€‹αž˜αžΆαž“β€‹αž‘αžΆαŸ†αž„β€‹αž’αžŸαŸ‹ αž€αžΆαžšαž”αŸ’αžšαžαž·αž”αžαŸ’αžαž·αž‚αŸ’αž˜αžΆαž“αž‘αžΈαž”αž‰αŸ’αž…αž”αŸ‹ αžŠαŸ†αžŽαžΎαžšαž€αžΆαžš ឬ (αžŠαŸ‚αž›αž€αžΎαžαž‘αžΎαž„αž‰αžΉαž€αž‰αžΆαž”αŸ‹αž‡αžΆαž„) αž‘αŸ… "αž‰αŸ‰αžΆαŸ†" αž€αžΆαžšαž…αž„αž…αžΆαŸ†αžŠαŸ‚αž›αž˜αžΆαž“αž‘αžΆαŸ†αž„αž’αžŸαŸ‹αŸ”.

PostgreSQL AntipatternsαŸ– β€œInfinity is not the limit!”, or a little about recursion
DBMS αž€αŸ’αž“αž»αž„αž“αŸαž™αž“αŸαŸ‡αž’αŸ’αžœαžΎαž€αžΆαžšαž›αžΎαž‚αŸ„αž›αž€αžΆαžšαžŽαŸαžŠαžΌαž…αž‚αŸ’αž“αžΆ - "αž‚αŸβ€‹αž”αŸ’αžšαžΆαž”αŸ‹β€‹αžαŸ’αž‰αž»αŸ†β€‹αž²αŸ’αž™β€‹αž‡αžΈαž€ αžŠαžΌαž…αŸ’αž“αŸαŸ‡β€‹αžαŸ’αž‰αž»αŸ†β€‹αž‡αžΈαž€"αŸ” αžŸαŸ†αžŽαžΎαžšαž”αžŸαŸ‹αž’αŸ’αž“αž€αž˜αž·αž“αžαŸ’αžšαžΉαž˜αžαŸ‚αž’αžΆαž…αž”αž“αŸ’αžαž™αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαžŠαŸ‚αž›αž“αŸ…αž‡αž·αžαžαžΆαž„αž“αŸ„αŸ‡αž‘αŸ αžŠαŸ„αž™αž‘αž‘αž½αž›αž™αž€αž’αž“αž’αžΆαž“αžšαž”αžŸαŸ‹ processor αž₯αžαžˆαž”αŸ‹αžˆαžš αž”αŸ‰αž»αž“αŸ’αžαŸ‚αžαŸ‚αž˜αž‘αžΆαŸ†αž„ "αž‘αž˜αŸ’αž›αžΆαž€αŸ‹" αž˜αžΌαž›αžŠαŸ’αž‹αžΆαž“αž‘αž·αž“αŸ’αž“αž“αŸαž™αž‘αžΆαŸ†αž„αž˜αžΌαž› "αž‰αŸ‰αžΆαŸ†" αž’αž„αŸ’αž‚αž…αž„αž…αžΆαŸ†αžŠαŸ‚αž›αž˜αžΆαž“αž‘αžΆαŸ†αž„αž’αžŸαŸ‹αŸ” αž€αžΆαžšαž€αžΆαžšαž–αžΆαžšαž”αŸ’αžšαž†αžΆαŸ†αž„αž“αžΉαž„αž€αžΆαžšαž€αžΎαžαž‘αžΎαž„αžœαž·αž‰αž‚αŸ’αž˜αžΆαž“αž€αŸ†αžŽαžαŸ‹ - αž‘αŸ†αž“αž½αž›αžαž»αžŸαžαŸ’αžšαžΌαžœαžšαž”αžŸαŸ‹αž’αŸ’αž“αž€αž’αž—αž·αžœαžŒαŸ’αžαž“αŸαžαŸ’αž›αž½αž“αž―αž„αŸ”

αž“αŸ…αž€αŸ’αž“αž»αž„ PostgreSQL αžŸαž˜αžαŸ’αžαž—αžΆαž–αž€αŸ’αž“αž»αž„αž€αžΆαžšαž”αŸ’αžšαžΎαžŸαŸ†αžŽαž½αžšαžŠαžŠαŸ‚αž›αŸ—αžαžΆαž˜αžšαž™αŸˆ WITH RECURSIVE αž”αžΆαž“αž”αž„αŸ’αž αžΆαž‰αžαŸ’αž›αž½αž“αž“αŸ…αž€αŸ’αž“αž»αž„αž‡αŸ†αž“αžΆαž“αŸ‹ 8.4 αžŠαŸ‚αž›αž˜αž·αž“αž’αŸ’αž›αžΆαž”αŸ‹αž˜αžΆαž“αž–αžΈαž˜αž»αž“αž˜αž€ αž”αŸ‰αž»αž“αŸ’αžαŸ‚αž’αŸ’αž“αž€αž“αŸ…αžαŸ‚αž’αžΆαž…αž‡αž½αž”αž”αŸ’αžšαž‘αŸ‡αžŸαŸ†αžŽαžΎ "αž‚αŸ’αž˜αžΆαž“αž€αžΆαžšαž€αžΆαžšαž–αžΆαžš" αžŠαŸ‚αž›αž’αžΆαž…αž„αžΆαž™αžšαž„αž‚αŸ’αžšαŸ„αŸ‡αž”αžΆαž“αž‡αžΆαž‘αŸ€αž„αž‘αžΆαžαŸ‹αŸ” αžαžΎαž’αŸ’αžœαžΎαžŠαžΌαž…αž˜αŸ’αžαŸαž…αžŠαžΎαž˜αŸ’αž”αžΈαž€αž˜αŸ’αž…αžΆαžαŸ‹αž”αž‰αŸ’αž αžΆαž”αŸ‚αž”αž“αŸαŸ‡?

αž€αž»αŸ†αžŸαžšαžŸαŸαžšαžŸαŸ†αžŽαž½αžšαžŠαžŠαŸ‚αž›αŸ—

αž αžΎαž™β€‹αžŸαžšαžŸαŸαžšβ€‹αž–αžΆαž€αŸ’αž™β€‹αžŠαŸ‚αž›β€‹αž˜αž·αž“β€‹αž”αŸ’αžšαžΎβ€‹αžŠαžŠαŸ‚αž›αŸ—αŸ” αžŠαŸ„αž™αž€αŸ’αžαžΈαž‚αŸ„αžšαž– K.O.

αžαžΆαž˜αž–αž·αž PostgreSQL αž•αŸ’αžαž›αŸ‹αž“αžΌαžœαž˜αž»αžαž„αžΆαžšαž‡αžΆαž…αŸ’αžšαžΎαž“αžŠαŸ‚αž›αž’αŸ’αž“αž€αž’αžΆαž…αž”αŸ’αžšαžΎ αž˜αž·αž“αž˜αžΆαž“ αž’αž“αž»αžœαžαŸ’αžαž€αžΆαžšαž αŸ…αž‘αžΎαž„αžœαž·αž‰αŸ”

αž”αŸ’αžšαžΎαžœαž·αž’αžΈαžŸαžΆαžŸαŸ’αžšαŸ’αžαž•αŸ’αžŸαŸαž„αž‚αŸ’αž“αžΆαž‡αžΆαž˜αžΌαž›αžŠαŸ’αž‹αžΆαž“αž…αŸ†αž–αŸ„αŸ‡αž”αž‰αŸ’αž αžΆ

αž–αŸαž›αžαŸ’αž›αŸ‡αž’αŸ’αž“αž€αž’αžΆαž…αž˜αžΎαž›αž”αž‰αŸ’αž αžΆαž–αžΈ "αž•αŸ’αž“αŸ‚αž€αž•αŸ’αžŸαŸαž„αž‚αŸ’αž“αžΆ" αŸ” αžαŸ’αž‰αž»αŸ†αž”αžΆαž“αž•αŸ’αžαž›αŸ‹αž§αž‘αžΆαž αžšαžŽαŸαž’αŸ†αž–αžΈαžŸαŸ’αžαžΆαž“αž—αžΆαž–αž”αŸ‚αž”αž“αŸαŸ‡αž“αŸ…αž€αŸ’αž“αž»αž„αž’αžαŸ’αžαž”αž‘ "SQL HowTo: 1000 αž“αž·αž„αžœαž·αž’αžΈαž˜αž½αž™αž“αŸƒαž€αžΆαžšαž”αŸ’αžšαž˜αžΌαž›αž•αŸ’αžαž»αŸ†" - αž‚αž»αžŽαž“αŸƒαžŸαŸ†αžŽαž»αŸ†αž›αŸαžαžŠαŸ„αž™αž˜αž·αž“αž”αŸ’αžšαžΎαž˜αž»αžαž„αžΆαžšαžŸαžšαž»αž”αž•αŸ’αž‘αžΆαž›αŸ‹αžαŸ’αž›αž½αž“αŸ–

WITH RECURSIVE src AS (
  SELECT '{2,3,5,7,11,13,17,19}'::integer[] arr
)
, T(i, val) AS (
  SELECT
    1::bigint
  , 1
UNION ALL
  SELECT
    i + 1
  , val * arr[i]
  FROM
    T
  , src
  WHERE
    i <= array_length(arr, 1)
)
SELECT
  val
FROM
  T
ORDER BY -- ΠΎΡ‚Π±ΠΎΡ€ Ρ„ΠΈΠ½Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°
  i DESC
LIMIT 1;

αžŸαŸ†αžŽαžΎαž“αŸαŸ‡αž’αžΆαž…αžαŸ’αžšαžΌαžœαž”αžΆαž“αž‡αŸ†αž“αž½αžŸαžŠαŸ„αž™αž‡αž˜αŸ’αžšαžΎαžŸαž–αžΈαž’αŸ’αž“αž€αž‡αŸ†αž“αžΆαž‰αž‚αžŽαž·αžαžœαž·αž‘αŸ’αž™αžΆαŸ–

WITH src AS (
  SELECT unnest('{2,3,5,7,11,13,17,19}'::integer[]) prime
)
SELECT
  exp(sum(ln(prime)))::integer val
FROM
  src;

αž”αŸ’αžšαžΎαžŸαŸŠαŸαžšαžΈ generate_series αž‡αŸ†αž“αž½αžŸαž±αŸ’αž™αžšαž„αŸ’αžœαž·αž›αž‡αž»αŸ†

αž§αž”αž˜αžΆαžαžΆαž™αžΎαž„αž”αŸ’αžšαžˆαž˜αž˜αž»αžαž“αžΉαž„αž—αžΆαžšαž€αž·αž…αŸ’αž…αž”αž„αŸ’αž€αžΎαžαž”αž»αž–αŸ’αžœαž”αž‘αžŠαŸ‚αž›αž’αžΆαž…αž’αŸ’αžœαžΎαž”αžΆαž“αž‘αžΆαŸ†αž„αž’αžŸαŸ‹αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αžαŸ’αžŸαŸ‚αž’αž€αŸ’αžŸαžš 'abcdefgh':

WITH RECURSIVE T AS (
  SELECT 'abcdefgh' str
UNION ALL
  SELECT
    substr(str, 1, length(str) - 1)
  FROM
    T
  WHERE
    length(str) > 1
)
TABLE T;

αžαžΎβ€‹αž’αŸ’αž“αž€β€‹αž”αŸ’αžšαžΆαž€αžŠβ€‹αžαžΆβ€‹αž’αŸ’αž“αž€β€‹αžαŸ’αžšαžΌαžœβ€‹αž€αžΆαžšβ€‹αž€αžΆαžšβ€‹αž αŸ…β€‹αž‘αžΎαž„αžœαž·αž‰β€‹αž“αŸ…β€‹αž‘αžΈαž“αŸαŸ‡β€‹?.. αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž’αŸ’αž“αž€β€‹αž”αŸ’αžšαžΎ LATERAL ΠΈ generate_seriesαž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αž’αŸ’αž“αž€αž“αžΉαž„αž˜αž·αž“αžαŸ’αžšαžΌαžœαž€αžΆαžš CTEαŸ–

SELECT
  substr(str, 1, ln) str
FROM
  (VALUES('abcdefgh')) T(str)
, LATERAL(
    SELECT generate_series(length(str), 1, -1) ln
  ) X;

αž•αŸ’αž›αžΆαžŸαŸ‹αž”αŸ’αžαžΌαžšαžšαž…αž“αžΆαžŸαž˜αŸ’αž–αŸαž“αŸ’αž’αž˜αžΌαž›αžŠαŸ’αž‹αžΆαž“αž‘αž·αž“αŸ’αž“αž“αŸαž™

αž‡αžΆαž§αž‘αžΆαž αžšαžŽαŸ αž’αŸ’αž“αž€αž˜αžΆαž“αžαžΆαžšαžΆαž„αž“αŸƒαžŸαžΆαžšαžœαŸαž‘αž·αž€αžΆαžŠαŸ‚αž›αž˜αžΆαž“αž‘αŸ†αž“αžΆαž€αŸ‹αž‘αŸ†αž“αž„αž–αžΈαž’αŸ’αž“αž€αžŽαžΆαž†αŸ’αž›αžΎαž™αžαž”αž‘αŸ…αž’αŸ’αž“αž€αžŽαžΆ αž¬αžαŸ’αžŸαŸ‚αžŸαŸ’αžšαž‘αžΆαž™αž…αžΌαž› αž”β€‹αžŽαŸ’αžαžΆβ€‹αž‰β€‹αžŸαž„αŸ’αž‚αž˜:

CREATE TABLE message(
  message_id
    uuid
      PRIMARY KEY
, reply_to
    uuid
      REFERENCES message
, body
    text
);
CREATE INDEX ON message(reply_to);

PostgreSQL AntipatternsαŸ– β€œInfinity is not the limit!”, or a little about recursion
αž‡αžΆαž€αžΆαžšαž”αŸ’αžšαžŸαžΎαžšαžŽαžΆαžŸαŸ‹ αžŸαŸ†αžŽαžΎαž’αž˜αŸ’αž˜αžαžΆαžŠαžΎαž˜αŸ’αž”αžΈαž‘αžΆαž‰αž™αž€αžŸαžΆαžšαž‘αžΆαŸ†αž„αž’αžŸαŸ‹αž›αžΎαž”αŸ’αžšαž’αžΆαž“αž”αž‘αž˜αž½αž™αž˜αžΎαž›αž‘αŸ…αžŠαžΌαž…αž“αŸαŸ‡αŸ–

WITH RECURSIVE T AS (
  SELECT
    *
  FROM
    message
  WHERE
    message_id = $1
UNION ALL
  SELECT
    m.*
  FROM
    T
  JOIN
    message m
      ON m.reply_to = T.message_id
)
TABLE T;

αž”αŸ‰αž»αž“αŸ’αžαŸ‚αžŠαŸ„αž™αžŸαžΆαžšαž™αžΎαž„αžαŸ‚αž„αžαŸ‚αžαŸ’αžšαžΌαžœαž€αžΆαžšαž”αŸ’αžšαž’αžΆαž“αž”αž‘αž‘αžΆαŸ†αž„αž˜αžΌαž›αž–αžΈαžŸαžΆαžšαž‡αžΆ root αžŠαžΌαž…αŸ’αž“αŸαŸ‡αž αŸαžαž»αž’αŸ’αžœαžΈαž”αžΆαž“αž‡αžΆαž™αžΎαž„αž˜αž·αž“αž’αŸ’αžœαžΎ αž”αž“αŸ’αžαŸ‚αž˜αž›αŸαžαžŸαž˜αŸ’αž‚αžΆαž›αŸ‹αžšαž”αžŸαŸ‹αžœαžΆαž‘αŸ…αž’αžΆαžαž»αž“αžΈαž˜αž½αž™αŸ— αžŸαŸ’αžœαŸαž™αž”αŸ’αžšαžœαžαŸ’αžαž·?

-- Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΏΠΎΠ»Π΅ с ΠΎΠ±Ρ‰ΠΈΠΌ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ Ρ‚Π΅ΠΌΡ‹ ΠΈ индСкс Π½Π° Π½Π΅Π³ΠΎ
ALTER TABLE message
  ADD COLUMN theme_id uuid;
CREATE INDEX ON message(theme_id);

-- ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Ρ‚Π΅ΠΌΡ‹ Π² Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Π΅ ΠΏΡ€ΠΈ вставкС
CREATE OR REPLACE FUNCTION ins() RETURNS TRIGGER AS $$
BEGIN
  NEW.theme_id = CASE
    WHEN NEW.reply_to IS NULL THEN NEW.message_id -- Π±Π΅Ρ€Π΅ΠΌ ΠΈΠ· стартового события
    ELSE ( -- ΠΈΠ»ΠΈ ΠΈΠ· сообщСния, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅ΠΌ
      SELECT
        theme_id
      FROM
        message
      WHERE
        message_id = NEW.reply_to
    )
  END;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER ins BEFORE INSERT
  ON message
    FOR EACH ROW
      EXECUTE PROCEDURE ins();

PostgreSQL AntipatternsαŸ– β€œInfinity is not the limit!”, or a little about recursion
αž₯αž‘αžΌαžœβ€‹αž“αŸαŸ‡β€‹αžŸαŸ†αžŽαž½αžšβ€‹αžŠαžŠαŸ‚αž›αŸ—β€‹αžšαž”αžŸαŸ‹β€‹αž™αžΎαž„β€‹αž‘αžΆαŸ†αž„β€‹αž˜αžΌαž›β€‹αž’αžΆαž…β€‹αžαŸ’αžšαžΌαžœβ€‹αž”αžΆαž“β€‹αž€αžΆαžαŸ‹β€‹αž”αž“αŸ’αžαž™β€‹αž˜αž€β€‹αžαŸ’αžšαžΉαž˜β€‹αžαŸ‚β€‹αž“αŸαŸ‡αŸ–

SELECT
  *
FROM
  message
WHERE
  theme_id = $1;

αž”αŸ’αžšαžΎ "αžŠαŸ‚αž“αž€αŸ†αžŽαžαŸ‹" αžŠαŸ‚αž›αž”αžΆαž“αž’αž“αž»αžœαžαŸ’αž

αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž™αžΎαž„αž˜αž·αž“αž’αžΆαž…αž•αŸ’αž›αžΆαžŸαŸ‹αž”αŸ’αžαžΌαžšαžšαž…αž“αžΆαžŸαž˜αŸ’αž–αŸαž“αŸ’αž’αž“αŸƒαž˜αžΌαž›αžŠαŸ’αž‹αžΆαž“αž‘αž·αž“αŸ’αž“αž“αŸαž™αžŠαŸ„αž™αž αŸαžαž»αž•αž›αž˜αž½αž™αž…αŸ†αž“αž½αž“ αžŸαžΌαž˜αž˜αžΎαž›αž’αŸ’αžœαžΈαžŠαŸ‚αž›αž™αžΎαž„αž’αžΆαž…αž–αžΉαž„αž•αŸ’αž’αŸ‚αž€αž›αžΎ αžŠαžΌαž…αŸ’αž“αŸαŸ‡αžŸαžΌαž˜αŸ’αž”αžΈαžαŸ‚αžœαžαŸ’αžαž˜αžΆαž“αž“αŸƒαž€αŸ†αž αž»αžŸαž“αŸ…αž€αŸ’αž“αž»αž„αž‘αž·αž“αŸ’αž“αž“αŸαž™αž€αŸαž˜αž·αž“αž“αžΆαŸ†αž±αŸ’αž™αž˜αžΆαž“αž€αžΆαžšαž€αžΎαžαž‘αžΎαž„αžŠαžŠαŸ‚αž›αŸ—αž‚αŸ’αž˜αžΆαž“αž‘αžΈαž”αž‰αŸ’αž…αž”αŸ‹αžŠαŸ‚αžšαŸ”

αž€αžΆαžšαž‚αžŽαž“αžΆαž‡αŸ†αžšαŸ…αž‘αžΎαž„αžœαž·αž‰

αž™αžΎαž„αž‚αŸ’αžšαžΆαž“αŸ‹αžαŸ‚αž”αž„αŸ’αž€αžΎαž“αž€αžΆαžšαžšαžΆαž”αŸ‹αž˜αŸ’αžαž„αž€αŸ’αž“αž»αž„αž˜αž½αž™αž‡αŸ†αž αžΆαž“αž“αŸƒαž€αžΆαžšαž”αž„αŸ’αž€αžΎαžαž‘αžΎαž„αžœαž·αž‰αž“αžΈαž˜αž½αž™αŸ— αžšαž αžΌαžαžŠαž›αŸ‹αž™αžΎαž„αžˆαžΆαž“αžŠαž›αŸ‹αžŠαŸ‚αž“αž€αŸ†αžŽαžαŸ‹αžŠαŸ‚αž›αž™αžΎαž„αž…αžΆαžαŸ‹αž‘αž»αž€αžαžΆαž˜αž·αž“αž‚αŸ’αžšαž”αŸ‹αž‚αŸ’αžšαžΆαž“αŸ‹αŸ–

WITH RECURSIVE T AS (
  SELECT
    0 i
  ...
UNION ALL
  SELECT
    i + 1
  ...
  WHERE
    T.i < 64 -- ΠΏΡ€Π΅Π΄Π΅Π»
)

αž‚αžΆαŸ†αž‘αŸ’αžš: αž“αŸ…αž–αŸαž›αžŠαŸ‚αž›αž™αžΎαž„αž–αŸ’αž™αžΆαž™αžΆαž˜αžšαž„αŸ’αžœαž·αž›αž‡αž»αŸ† αž™αžΎαž„αž“αžΉαž„αž“αŸ…αžαŸ‚αž’αŸ’αžœαžΎαž˜αž·αž“αž›αžΎαžŸαž–αžΈαžŠαŸ‚αž“αž€αŸ†αžŽαžαŸ‹αžŠαŸ‚αž›αž”αžΆαž“αž”αž‰αŸ’αž‡αžΆαž€αŸ‹αž“αŸƒαž€αžΆαžšαž’αŸ’αžœαžΎαž‘αžΎαž„αžœαž·αž‰ "αž“αŸ…αž€αŸ’αž“αž»αž„αž‡αž˜αŸ’αžšαŸ…" αŸ”
αž‚αž»αžŽαžœαž·αž”αžαŸ’αžαž·: αž˜αž·αž“αž˜αžΆαž“αž€αžΆαžšαž’αžΆαž“αžΆαžαžΆαž™αžΎαž„αž“αžΉαž„αž˜αž·αž“αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž€αŸ†αžŽαžαŸ‹αžαŸ’αžšαžΆαžŠαžŠαŸ‚αž›αž˜αŸ’αžαž„αž‘αŸ€αžαž‘αŸ - αž§αž‘αžΆαž αžšαžŽαŸαž“αŸ…αž‡αž˜αŸ’αžšαŸ… 15 αž“αž·αž„ 25 αž αžΎαž™αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αžšαŸ€αž„αžšαžΆαž›αŸ‹ +10 αŸ” αž αžΎαž™αž‚αŸ’αž˜αžΆαž“αž“αžšαžŽαžΆαž˜αŸ’αž“αžΆαž€αŸ‹αžŸαž“αŸ’αž™αžΆαž’αŸ’αžœαžΈαž’αŸ†αž–αžΈ "αž‘αž‘αžΉαž„" αž‘αŸαŸ”

αž‡αžΆβ€‹αž•αŸ’αž›αžΌαžœαž€αžΆαžš αž€αžΆαžšβ€‹αž”αž„αŸ’αž€αžΎαžβ€‹αž‘αžΎαž„αžœαž·αž‰β€‹αž”αŸ‚αž”β€‹αž“αŸαŸ‡β€‹αž“αžΉαž„β€‹αž˜αž·αž“β€‹αž˜αžΆαž“β€‹αž€αŸ†αžŽαžαŸ‹β€‹αž‘αŸ αž”αŸ‰αž»αž“αŸ’αžαŸ‚β€‹αž”αŸ’αžšαžŸαž·αž“αž”αžΎβ€‹αž“αŸ…β€‹αž‡αŸ†αž αžΆαž“β€‹αž“αžΈαž˜αž½αž™αŸ—β€‹αž…αŸ†αž“αž½αž“β€‹αž“αŸƒβ€‹αž€αŸ†αžŽαžαŸ‹αžαŸ’αžšαžΆβ€‹αž€αžΎαž“αž‘αžΎαž„β€‹αž‡αžΆβ€‹αž“αž·αž‘αžŸαŸ’αžŸαž“αŸ’αž αž™αžΎαž„β€‹αž‘αžΆαŸ†αž„αž’αžŸαŸ‹β€‹αž‚αŸ’αž“αžΆβ€‹αžŠαžΉαž„β€‹αž…αŸ’αž”αžΆαžŸαŸ‹β€‹αžαžΆβ€‹αžœαžΆβ€‹αž”αž‰αŸ’αž…αž”αŸ‹β€‹αžŠαŸ„αž™β€‹αžšαž”αŸ€αž”β€‹αžŽαžΆ...

PostgreSQL AntipatternsαŸ– β€œInfinity is not the limit!”, or a little about recursionαžŸαžΌαž˜αž˜αžΎαž› "αž”αž‰αŸ’αž αžΆαž‚αŸ’αžšαžΆαž”αŸ‹αž’αž‰αŸ’αž‰αž‡αžΆαžαž·αž“αŸ…αž›αžΎαž€αŸ’αžαžΆαžšαž’αž»αž€"

αž’αžΆαžŽαžΆαž–αŸ’αž™αžΆαž”αžΆαž›αž“αŸƒ "αž•αŸ’αž›αžΌαžœ"

αž™αžΎαž„αž†αŸ’αž›αžΆαžŸαŸ‹αž‚αŸ’αž“αžΆαž”αž“αŸ’αžαŸ‚αž˜αž§αž”αž€αžšαžŽαŸαž€αŸ†αžŽαžαŸ‹αž’αžαŸ’αžαžŸαž‰αŸ’αž‰αžΆαžŽαžœαžαŸ’αžαž»αž‘αžΆαŸ†αž„αž’αžŸαŸ‹αžŠαŸ‚αž›αž™αžΎαž„αž‡αž½αž”αž”αŸ’αžšαž‘αŸ‡αž“αŸ…αžαžΆαž˜αž”αžŽαŸ’αžαŸ„αž™αž•αŸ’αž›αžΌαžœαž”αž„αŸ’αž€αžΎαžαž‘αžΎαž„αžœαž·αž‰αž‘αŸ…αž€αŸ’αž“αž»αž„αž’αžΆαžšαŸαž˜αž½αž™ αžŠαŸ‚αž›αž‡αžΆ "αž•αŸ’αž›αžΌαžœ" αžαŸ‚αž˜αž½αž™αž‚αžαŸ‹αž‘αŸ…αžœαžΆαŸ–

WITH RECURSIVE T AS (
  SELECT
    ARRAY[id] path
  ...
UNION ALL
  SELECT
    path || id
  ...
  WHERE
    id <> ALL(T.path) -- Π½Π΅ совпадаСт Π½ΠΈ с ΠΎΠ΄Π½ΠΈΠΌ ΠΈΠ·
)

αž‚αžΆαŸ†αž‘αŸ’αžš: αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž˜αžΆαž“αžœαžŠαŸ’αžŠαž“αŸ…αž€αŸ’αž“αž»αž„αž‘αž·αž“αŸ’αž“αž“αŸαž™ αž™αžΎαž„αž“αžΉαž„αž˜αž·αž“αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž€αŸ†αžŽαžαŸ‹αžαŸ’αžšαžΆαžŠαžŠαŸ‚αž›αžŠαžŠαŸ‚αž›αŸ—αž“αŸ…αž€αŸ’αž“αž»αž„αž•αŸ’αž›αžΌαžœαžŠαžŠαŸ‚αž›αž“αŸ„αŸ‡αž‘αŸαŸ”
αž‚αž»αžŽαžœαž·αž”αžαŸ’αžαž·: αž”αŸ‰αž»αž“αŸ’αžαŸ‚αž€αŸ’αž“αž»αž„αž–αŸαž›αž‡αžΆαž˜αž½αž™αž‚αŸ’αž“αžΆαž“αŸαŸ‡ αž™αžΎαž„αž’αžΆαž…αžšαŸ†αž›αž„αž€αŸ†αžŽαžαŸ‹αžαŸ’αžšαžΆαž‘αžΆαŸ†αž„αž’αžŸαŸ‹αžŠαŸ„αž™αž–αŸ’αž™αž‰αŸ’αž‡αž“αŸˆαžŠαŸ„αž™αž˜αž·αž“αž’αŸ’αžœαžΎαž˜αŸ’αžαž„αž‘αŸ€αžαžŠαŸ„αž™αžαŸ’αž›αž½αž“αž―αž„αŸ”

PostgreSQL AntipatternsαŸ– β€œInfinity is not the limit!”, or a little about recursionαžŸαžΌαž˜αž˜αžΎαž› "αž”αž‰αŸ’αž αžΆαž•αŸ’αž›αžΆαžŸαŸ‹αž‘αžΈαžšαž”αžŸαŸ‹ Knight"

αžŠαŸ‚αž“αž€αŸ†αžŽαžαŸ‹αž”αŸ’αžšαžœαŸ‚αž„αž•αŸ’αž›αžΌαžœ

αžŠαžΎαž˜αŸ’αž”αžΈαž‡αŸ€αžŸαžœαžΆαž„αžŸαŸ’αžαžΆαž“αž—αžΆαž–αž“αŸƒαž€αžΆαžšαž“αž·αž™αžΆαž™αž‘αžΎαž„αžœαž·αž‰ "αžœαž„αŸ’αžœαŸαž„" αž“αŸ…αž‡αž˜αŸ’αžšαŸ…αžŠαŸ‚αž›αž˜αž·αž“αž’αžΆαž…αž™αž›αŸ‹αž”αžΆαž“ αž™αžΎαž„αž’αžΆαž…αž”αž‰αŸ’αž…αžΌαž›αž‚αŸ’αž“αžΆαž“αžΌαžœαžœαž·αž’αžΈαž–αžΈαžšαž˜αž»αž“αž“αŸαŸ‡αŸ” αž¬αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž™αžΎαž„αž˜αž·αž“αž…αž„αŸ‹αž‚αžΆαŸ†αž‘αŸ’αžšαžœαžΆαž›αžŠαŸ‚αž›αž˜αž·αž“αž…αžΆαŸ†αž”αžΆαž…αŸ‹ αžŸαžΌαž˜αž”αž“αŸ’αžαŸ‚αž˜αž›αž€αŸ’αžαžαžŽαŸ’αžŒαžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž€αžΆαžšαž”αž“αŸ’αžαž€αžΆαžšαž αŸ…αž‘αžΎαž„αžœαž·αž‰αžŠαŸ„αž™αž”αŸ‰αžΆαž“αŸ‹αžŸαŸ’αž˜αžΆαž“αž”αŸ’αžšαžœαŸ‚αž„αž•αŸ’αž›αžΌαžœαŸ–

WITH RECURSIVE T AS (
  SELECT
    ARRAY[id] path
  ...
UNION ALL
  SELECT
    path || id
  ...
  WHERE
    id <> ALL(T.path) AND
    array_length(T.path, 1) < 10
)

αž‡αŸ’αžšαžΎαžŸαžšαžΎαžŸαžœαž·αž’αžΈαžŠαžΎαž˜αŸ’αž”αžΈαžšαžŸαž‡αžΆαžαž·αžšαž”αžŸαŸ‹αž’αŸ’αž“αž€!

αž”αŸ’αžšαž—αž–: www.habr.com

αž”αž“αŸ’αžαŸ‚αž˜αž˜αžαž·αž™αŸ„αž”αž›αŸ‹