PostgreSQL Antipatterns: "ืื™ื ืกื•ืฃ ื”ื•ื ืœื ื”ื’ื‘ื•ืœ!", ืื• ืงืฆืช ืขืœ ืจืงื•ืจืกื™ื”

ืจืงื•ืจืกื™ื” - ืžื ื’ื ื•ืŸ ื—ื–ืง ื•ื ื•ื— ืžืื•ื“ ืื ืžื‘ื•ืฆืขื•ืช ืื•ืชืŸ ืคืขื•ืœื•ืช "ืžืขืžื™ืงื•ืช" ืขืœ ื ืชื•ื ื™ื ืงืฉื•ืจื™ื. ืื‘ืœ ืจืงื•ืจืกื™ื” ื‘ืœืชื™ ืžื‘ื•ืงืจืช ื”ื™ื ืจื•ืข ืฉื™ื›ื•ืœ ืœื”ื•ื‘ื™ืœ ืœืฉื ื™ื”ื ื‘ื™ืฆื•ืข ืื™ื ืกื•ืคื™ ืชื”ืœื™ืš, ืื• (ืžื” ืฉืงื•ืจื” ืœืขืชื™ื ืงืจื•ื‘ื•ืช ื™ื•ืชืจ). "ืื•ื›ืœื™ื" ืืช ื›ืœ ื”ื–ื™ื›ืจื•ืŸ ื”ืคื ื•ื™.

PostgreSQL Antipatterns: "ืื™ื ืกื•ืฃ ื”ื•ื ืœื ื”ื’ื‘ื•ืœ!", ืื• ืงืฆืช ืขืœ ืจืงื•ืจืกื™ื”
DBMS ื‘ื”ืงืฉืจ ื–ื” ืขื•ื‘ื“ ืขืœ ืื•ืชื ืขืงืจื•ื ื•ืช - "ื”ื ืืžืจื• ืœื™ ืœื—ืคื•ืจ, ืื– ืื ื™ ื—ื•ืคืจ". ื”ื‘ืงืฉื” ืฉืœืš ื™ื›ื•ืœื” ืœื ืจืง ืœื”ืื˜ ืชื”ืœื™ื›ื™ื ืฉื›ื ื™ื, ืœืชืคื•ืก ื›ืœ ื”ื–ืžืŸ ืžืฉืื‘ื™ ืžืขื‘ื“, ืืœื ื’ื "ืœื”ืคื™ืœ" ืืช ื›ืœ ืžืกื“ ื”ื ืชื•ื ื™ื, "ืœืื›ื•ืœ" ืืช ื›ืœ ื”ื–ื™ื›ืจื•ืŸ ื”ื–ืžื™ืŸ. ืœื›ืŸ ื”ื’ื ื” ืžืคื ื™ ืจืงื•ืจืกื™ื” ืื™ื ืกื•ืคื™ืช - ืื—ืจื™ื•ืช ื”ื™ื–ื ืขืฆืžื•.

ื‘-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;

ื”ืฉืชืžืฉ ื‘-generated_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: "ืื™ื ืกื•ืฃ ื”ื•ื ืœื ื”ื’ื‘ื•ืœ!", ืื• ืงืฆืช ืขืœ ืจืงื•ืจืกื™ื”
ื•ื‘ื›ืŸ, ื‘ืงืฉื” ื˜ื™ืคื•ืกื™ืช ืœื”ื•ืจื™ื“ ืืช ื›ืœ ื”ื”ื•ื“ืขื•ืช ื‘ื ื•ืฉื ืื—ื“ ื ืจืื™ืช ื‘ืขืจืš ื›ืš:

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;

ืื‘ืœ ืžื›ื™ื•ื•ืŸ ืฉืื ื—ื ื• ืชืžื™ื“ ืฆืจื™ื›ื™ื ืืช ื›ืœ ื”ื ื•ืฉื ืžื”ื”ื•ื“ืขื” ื”ื‘ืกื™ืกื™ืช, ืื– ืœืžื” ืฉืœื ื”ื•ืกืฃ ืืช ื”ืžื–ื”ื” ืฉืœื• ืœื›ืœ ืขืจืš ืื•ึนื˜ื•ึนืžึธื˜ึดื™?

-- ะดะพะฑะฐะฒะธะผ ะฟะพะปะต ั ะพะฑั‰ะธะผ ะธะดะตะฝั‚ะธั„ะธะบะฐั‚ะพั€ะพะผ ั‚ะตะผั‹ ะธ ะธะฝะดะตะบั ะฝะฐ ะฝะตะณะพ
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: "ืื™ื ืกื•ืฃ ื”ื•ื ืœื ื”ื’ื‘ื•ืœ!", ืื• ืงืฆืช ืขืœ ืจืงื•ืจืกื™ื”
ื›ืขืช ื ื™ืชืŸ ืœืฆืžืฆื ืืช ื›ืœ ื”ืฉืื™ืœืชื” ื”ืจืงื•ืจืกื™ื‘ื™ืช ืฉืœื ื• ืจืง ืœื–ื”:

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: "ืื™ื ืกื•ืฃ ื”ื•ื ืœื ื”ื’ื‘ื•ืœ!", ืื• ืงืฆืช ืขืœ ืจืงื•ืจืกื™ื”ืจืื” "ื‘ืขื™ื™ืช ื”ื“ื’ื ื™ื ืขืœ ืœื•ื— ืฉื—ืžื˜"

ืฉื•ืžืจ "ื”ื ืชื™ื‘"

ืื ื• ืžื•ืกื™ืคื™ื ืœืกื™ืจื•ื’ื™ืŸ ืืช ื›ืœ ืžื–ื”ื™ ื”ืื•ื‘ื™ื™ืงื˜ื™ื ืฉืคื’ืฉื ื• ืœืื•ืจืš ื ืชื™ื‘ ื”ืจืงื•ืจืกื™ื” ืœืžืขืจืš, ืฉื”ื•ื "ื ืชื™ื‘" ื™ื™ื—ื•ื“ื™ ืืœื™ื•:

WITH RECURSIVE T AS (
  SELECT
    ARRAY[id] path
  ...
UNION ALL
  SELECT
    path || id
  ...
  WHERE
    id <> ALL(T.path) -- ะฝะต ัะพะฒะฟะฐะดะฐะตั‚ ะฝะธ ั ะพะดะฝะธะผ ะธะท
)

ืคืจื•: ืื ื™ืฉ ืžื—ื–ื•ืจ ื‘ื ืชื•ื ื™ื, ืื ื• ืœื—ืœื•ื˜ื™ืŸ ืœื ื ืขื‘ื“ ืืช ืื•ืชื” ืจืฉื•ืžื” ืฉื•ื‘ ื•ืฉื•ื‘ ื‘ืื•ืชื• ื ืชื™ื‘.
ื—ืกืจื•ื ื•ืช: ืื‘ืœ ื™ื—ื“ ืขื ื–ืืช, ืื ื—ื ื• ื™ื›ื•ืœื™ื ืžืžืฉ ืœืขืงื•ืฃ ืืช ื›ืœ ื”ืจืฉื•ืžื•ืช ืžื‘ืœื™ ืœื—ื–ื•ืจ ืขืœ ืขืฆืžื ื•.

PostgreSQL Antipatterns: "ืื™ื ืกื•ืฃ ื”ื•ื ืœื ื”ื’ื‘ื•ืœ!", ืื• ืงืฆืช ืขืœ ืจืงื•ืจืกื™ื”ืจืื” "ื‘ืขื™ื™ืช ืžื”ืœืš ื”ืื‘ื™ืจ"

ืžื’ื‘ืœืช ืื•ืจืš ื ืชื™ื‘

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

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

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