PostgreSQL Antipatterns: ื”ืขืจื›ืช ืžืฆื‘ ื‘-SQL

SQL ืื™ื ื• C++, ื•ืœื JavaScript. ืœื›ืŸ, ื”ื—ื™ืฉื•ื‘ ืฉืœ ื‘ื™ื˜ื•ื™ื™ื ืœื•ื’ื™ื™ื ืžืชืจื—ืฉ ืื—ืจืช, ื•ื–ื” ื‘ื›ืœืœ ืœื ืื•ืชื• ื“ื‘ืจ:

WHERE fncondX() AND fncondY()

= fncondX() && fncondY()

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

ืœื›ืŸ, ืื ืืชื” ืขื“ื™ื™ืŸ ืจื•ืฆื” ืœื ื”ืœ ืขื“ื™ืคื•ืช, ืืชื” ืฆืจื™ืš ืœื‘ื ื•ืช ืื•ืชื” ืœื”ืคื•ืš ืืช ื”ืชื ืื™ื ื”ืœืœื• ืœืœื ืฉื•ื•ื™ื ื‘ืืžืฆืขื•ืช ืชื ืื™ื ื‘ื™ื˜ื•ื™ื™ื ะธ ืื•ืคืจื˜ื•ืจื™ื.

PostgreSQL Antipatterns: ื”ืขืจื›ืช ืžืฆื‘ ื‘-SQL
ื”ื ืชื•ื ื™ื ื•ื”ืขื‘ื•ื“ื” ืื™ืชื ื”ื ื”ื‘ืกื™ืก ืžืชื—ื ื”-VLSI ืฉืœื ื•, ืœื›ืŸ ื—ืฉื•ื‘ ืœื ื• ืžืื•ื“ ืฉื”ืคืขื•ืœื•ืช ื‘ื”ื ื™ื‘ื•ืฆืขื• ืœื ืจืง ื‘ืฆื•ืจื” ื ื›ื•ื ื”, ืืœื ื’ื ื‘ื™ืขื™ืœื•ืช. ื‘ื•ืื• ื ืกืชื›ืœ ืขืœ ื“ื•ื’ืžืื•ืช ืกืคืฆื™ืคื™ื•ืช ืฉื‘ื”ืŸ ื ื™ืชืŸ ืœืขืฉื•ืช ืฉื’ื™ืื•ืช ื‘ื—ื™ืฉื•ื‘ ื‘ื™ื˜ื•ื™ื™ื, ื•ื”ื™ื›ืŸ ื›ื“ืื™ ืœืฉืคืจ ืืช ื™ืขื™ืœื•ืชืŸ.

#0: RTFM

ืžืชื—ื™ืœ ื“ื•ื’ืžื” ืžืชื•ืš ืชื™ืขื•ื“:

ื›ืืฉืจ ืกื“ืจ ื”ื”ืขืจื›ื” ื—ืฉื•ื‘, ื ื™ืชืŸ ืœืœื›ื•ื“ ืื•ืชื• ื‘ืืžืฆืขื•ืช ื”ืžื‘ื ื” CASE. ืœื“ื•ื’ืžื”, ื–ื•ื”ื™ ื“ืจืš ืœื”ื™ืžื ืข ืžื—ืœื•ืงื” ื‘ืืคืก ื‘ืžืฉืคื˜ WHERE ืœึนื ืžึฐื”ึตื™ืžึธืŸ:

SELECT ... WHERE x > 0 AND y/x > 1.5;

ืืคืฉืจื•ืช ื‘ื˜ื•ื—ื”:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;

ื”ืขื™ืฆื•ื‘ ื”ืžืฉืžืฉ ื‘ื“ืจืš ื–ื• CASE ืžื’ืŸ ืขืœ ื”ื‘ื™ื˜ื•ื™ ืžืื•ืคื˜ื™ืžื™ื–ืฆื™ื”, ืœื›ืŸ ื™ืฉ ืœื”ืฉืชืžืฉ ื‘ื• ืจืง ื›ืืฉืจ ื™ืฉ ืฆื•ืจืš.

ืžืก' 1: ืžืฆื‘ ื˜ืจื™ื’ืจ

BEGIN
  IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
    ...
  END IF;
  RETURN NEW;
END;

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

BEGIN
  IF cond(NEW.fld) THEN
    IF EXISTS(SELECT ...) THEN
      ...
    END IF;
  END IF;
  RETURN NEW;
END;

ืขื›ืฉื™ื• ื‘ื•ืื• ื ืกืชื›ืœ ื”ื™ื˜ื‘ - ื›ืœ ื”ื’ื•ืฃ ืฉืœ ืคื•ื ืงืฆื™ื™ืช ื”ื”ื“ืง "ืขื˜ื•ืฃ" ืคื ื™ืžื” IF. ื–ื” ืื•ืžืจ ืฉืฉื•ื ื“ื‘ืจ ืœื ืžื•ื ืข ืžืื™ืชื ื• ืœื”ืกื™ืจ ืžืฆื‘ ื–ื” ืžื”ื”ืœื™ืš ื‘ืืžืฆืขื•ืช WHEN-ืชื ืื™ื:

BEGIN
  IF EXISTS(SELECT ...) THEN
    ...
  END IF;
  RETURN NEW;
END;
...
CREATE TRIGGER ...
  WHEN cond(NEW.fld);

ื’ื™ืฉื” ื–ื• ืžื•ื‘ื˜ื—ืช ืœื—ืกื•ืš ืžืฉืื‘ื™ ืฉืจืช ื›ืืฉืจ ื”ืชื ืื™ ื”ื•ื ืฉืงืจื™.

#2: ืฉืจืฉืจืช OR/AND

SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)

ืื—ืจืช, ืืชื” ื™ื›ื•ืœ ืœื’ืžื•ืจ ืขื ืฉื ื™ื”ื EXISTS ื™ื”ื™ื” "ื ื›ื•ืŸ", ืื‘ืœ ืฉื ื™ื”ื ื™ืชื’ืฉืžื•.

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

ืžืกืชื‘ืจ ืฉื–ื” ืืคืฉืจื™ - ื”ื’ื™ืฉื” ื”ืืœื’ื•ืจื™ืชืžื™ืช ืงืจื•ื‘ื” ืœื ื•ืฉื ื”ืžืืžืจ PostgreSQL Antipatterns: ืฉื™ื ื ื“ื™ืจ ื™ื’ื™ืข ืœืืžืฆืข JOIN.

ื‘ื•ืื• ืคืฉื•ื˜ "ื ื“ื—ื•ืฃ" ืืช ืฉื ื™ ื”ืชื ืื™ื ื”ืœืœื• ืชื—ืช CASE:

SELECT ...
WHERE
  CASE
    WHEN EXISTS(... A) THEN TRUE
    WHEN EXISTS(... B) THEN TRUE
  END

ื‘ืžืงืจื” ื”ื–ื” ืœื ื”ื’ื“ืจื ื• ELSE-ืขืจืš, ื›ืœื•ืžืจ ืื ืฉื ื™ ื”ืชื ืื™ื ืฉืงืจื™ื™ื CASE ื™ื—ื–ื•ืจ NULL, ืฉืžืชืคืจืฉ ื› FALSE ะฒ WHERE-ืชื ืื™ื.

ื ื™ืชืŸ ืœืฉืœื‘ ื“ื•ื’ืžื” ื–ื• ื‘ื“ืจื›ื™ื ืื—ืจื•ืช - ืชืœื•ื™ ื‘ื˜ืขื ื•ื‘ืฆื‘ืข:

SELECT ...
WHERE
  CASE
    WHEN NOT EXISTS(... A) THEN EXISTS(... B)
    ELSE TRUE
  END

#3: ืื™ืš [ืœื] ืœื›ืชื•ื‘ ืชื ืื™ื

ื‘ื™ืœื™ื ื• ื™ื•ืžื™ื™ื ื‘ื ื™ืชื•ื— ื”ืกื™ื‘ื•ืช ืœืคืขื•ืœื” ื”"ืžื•ื–ืจื”" ืฉืœ ื”ื“ืง ื”ื–ื” - ื‘ื•ืื• ื ืจืื” ืœืžื”.

ืžึธืงื•ึนืจ:

IF( NEW."ะ”ะพะบัƒะผะตะฝั‚_" is null or NEW."ะ”ะพะบัƒะผะตะฝั‚_" = (select '"ะšะพะผะฟะปะตะบั‚"'::regclass::oid) or NEW."ะ”ะพะบัƒะผะตะฝั‚_" = (select to_regclass('"ะ”ะพะบัƒะผะตะฝั‚ะŸะพะ—ะฐั€ะฟะปะฐั‚ะต"')::oid)
     AND (   OLD."ะ”ะพะบัƒะผะตะฝั‚ะะฐัˆะฐะžั€ะณะฐะฝะธะทะฐั†ะธั" <> NEW."ะ”ะพะบัƒะผะตะฝั‚ะะฐัˆะฐะžั€ะณะฐะฝะธะทะฐั†ะธั"
          OR OLD."ะฃะดะฐะปะตะฝ" <> NEW."ะฃะดะฐะปะตะฝ"
          OR OLD."ะ”ะฐั‚ะฐ" <> NEW."ะ”ะฐั‚ะฐ"
          OR OLD."ะ’ั€ะตะผั" <> NEW."ะ’ั€ะตะผั"
          OR OLD."ะ›ะธั†ะพะกะพะทะดะฐะป" <> NEW."ะ›ะธั†ะพะกะพะทะดะฐะป" ) ) THEN ...

ื‘ืขื™ื” ืžืก' 1: ืื™ ื”ืฉื•ื•ื™ื•ืŸ ืœื ืžื›ื‘ื“ ืืช NULL

ื‘ื•ืื• ื ื“ืžื™ื™ืŸ ืฉื”ื›ืœ OLDืœืฉื“ื•ืช ื”ื™ื™ืชื” ืžืฉืžืขื•ืช NULL. ืžื” ื™ืงืจื”?

SELECT NULL <> 1 OR NULL <> 2;
-- NULL

ื•ืžื ืงื•ื“ืช ืžื‘ื˜ ืฉืœ ืขื‘ื•ื“ื” ืขืœ ื”ืชื ืื™ื NULL ืฉื•ื•ื” ืขืจืš FALSE, ื›ืžื•ื–ื›ืจ ืœืขื™ืœ.

ื”ื—ืœื˜ื”: ื”ืฉืชืžืฉ ื‘ืื•ืคืจื˜ื•ืจ IS DISTINCT FROM ืž ROW-ืžืคืขื™ืœ, ืžืฉื•ื•ื” ืจืฉื•ืžื•ืช ืฉืœืžื•ืช ื‘ื‘ืช ืื—ืช:

SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE

ื‘ืขื™ื” ืžืก' 2: ื™ื™ืฉื•ืžื™ื ืฉื•ื ื™ื ืฉืœ ืื•ืชื” ืคื•ื ืงืฆื™ื•ื ืœื™ื•ืช

ื‘ื•ืื• ื ืฉื•ื•ื”:

NEW."ะ”ะพะบัƒะผะตะฝั‚_" = (select '"ะšะพะผะฟะปะตะบั‚"'::regclass::oid)
NEW."ะ”ะพะบัƒะผะตะฝั‚_" = (select to_regclass('"ะ”ะพะบัƒะผะตะฝั‚ะŸะพะ—ะฐั€ะฟะปะฐั‚ะต"')::oid)

ืœืžื” ื™ืฉ ื›ืืŸ ื”ืฉืงืขื” ื ื•ืกืคืช? SELECT? ืชืคืงื•ื“ to_regclass? ืœืžื” ื–ื” ืฉื•ื ื”?..

ื‘ื•ืื• ื ืชืงืŸ:

NEW."ะ”ะพะบัƒะผะตะฝั‚_" = '"ะšะพะผะฟะปะตะบั‚"'::regclass::oid
NEW."ะ”ะพะบัƒะผะตะฝั‚_" = '"ะ”ะพะบัƒะผะตะฝั‚ะŸะพะ—ะฐั€ะฟะปะฐั‚ะต"'::regclass::oid

ื‘ืขื™ื” ืžืก' 3: ืขื“ื™ืคื•ืช ืฉืœ ืคืขื•ืœื•ืช bool

ื‘ื•ืื• ื ืขืฆื‘ ืืช ื”ืžืงื•ืจ:

{... IS NULL} OR
{... ะšะพะผะฟะปะตะบั‚} OR
{... ะ”ะพะบัƒะผะตะฝั‚ะŸะพะ—ะฐั€ะฟะปะฐั‚ะต} AND
( {... ะฝะตั€ะฐะฒะตะฝัั‚ะฒะฐ} )

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

ื‘ื•ืื• ื ืชืงืŸ:

(
  {... IS NULL} OR
  {... ะšะพะผะฟะปะตะบั‚} OR
  {... ะ”ะพะบัƒะผะตะฝั‚ะŸะพะ—ะฐั€ะฟะปะฐั‚ะต}
) AND
( {... ะฝะตั€ะฐะฒะตะฝัั‚ะฒะฐ} )

ื‘ืขื™ื” ืžืก' 4 (ืงื˜ื ื”): ืชื ืื™ OR ืžื•ืจื›ื‘ ืขื‘ื•ืจ ืฉื“ื” ืื—ื“

ืœืžืขืฉื”, ื”ื™ื• ืœื ื• ื‘ืขื™ื•ืช ื‘ืžืก' 3 ื‘ื“ื™ื•ืง ื‘ื’ืœืœ ืฉื”ื™ื• ืฉืœื•ืฉื” ืชื ืื™ื. ืื‘ืœ ื‘ืžืงื•ื ืื•ืชื ืืชื” ื™ื›ื•ืœ ืœื”ืกืชื“ืจ ืขื ืื—ื“, ื‘ืืžืฆืขื•ืช ื”ืžื ื’ื ื•ืŸ coalesce ... IN:

coalesce(NEW."ะ”ะพะบัƒะผะตะฝั‚_"::text, '') IN ('', '"ะšะพะผะฟะปะตะบั‚"', '"ะ”ะพะบัƒะผะตะฝั‚ะŸะพะ—ะฐั€ะฟะปะฐั‚ะต"')

ืื– ืื ื—ื ื• NULL "ื ืชืคื•ืก", ื•ืงืฉื” OR ืื™ืŸ ืฆื•ืจืš ืœื’ื“ืจ ื‘ืกื•ื’ืจื™ื™ื.

ื‘ืกืš ื”ื›ืœ

ื‘ื•ืื• ื ืจืฉื•ื ืžื” ืงื™ื‘ืœื ื•:

IF (
  coalesce(NEW."ะ”ะพะบัƒะผะตะฝั‚_"::text, '') IN ('', '"ะšะพะผะฟะปะตะบั‚"', '"ะ”ะพะบัƒะผะตะฝั‚ะŸะพะ—ะฐั€ะฟะปะฐั‚ะต"') AND
  (
    OLD."ะ”ะพะบัƒะผะตะฝั‚ะะฐัˆะฐะžั€ะณะฐะฝะธะทะฐั†ะธั"
  , OLD."ะฃะดะฐะปะตะฝ"
  , OLD."ะ”ะฐั‚ะฐ"
  , OLD."ะ’ั€ะตะผั"
  , OLD."ะ›ะธั†ะพะกะพะทะดะฐะป"
  ) IS DISTINCT FROM (
    NEW."ะ”ะพะบัƒะผะตะฝั‚ะะฐัˆะฐะžั€ะณะฐะฝะธะทะฐั†ะธั"
  , NEW."ะฃะดะฐะปะตะฝ"
  , NEW."ะ”ะฐั‚ะฐ"
  , NEW."ะ’ั€ะตะผั"
  , NEW."ะ›ะธั†ะพะกะพะทะดะฐะป"
  )
) THEN ...

ื•ืื ืืชื” ืžื—ืฉื™ื‘ ืฉื ื™ืชืŸ ืœื”ืฉืชืžืฉ ื‘ืคื•ื ืงืฆื™ื™ืช ื˜ืจื™ื’ืจ ื–ื• ืจืง ื‘ UPDATE-ื˜ืจื™ื’ืจ ืขืงื‘ ื–ืžื™ื ื•ืช OLD/NEW ื‘ืžืฆื‘ ื‘ืจืžื” ื”ืขืœื™ื•ื ื”, ืื– ื‘ื“ืจืš ื›ืœืœ ื ื™ืชืŸ ืœื”ืฆื™ื‘ ืžืฆื‘ ื–ื” WHEN-ืžืฆื‘, ื›ืคื™ ืฉืžื•ืฆื’ ื‘ืžืกืคืจ 1...

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

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