PostgreSQL Antipatterns: JOINs ื•-ORs ืžื–ื™ืงื™ื

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

ื‘ื—ืœืง ืžื”ื’ืจืกืื•ืช ื”ื‘ืื•ืช ืฉืœ PG ื”ืžืฆื‘ ืขืฉื•ื™ ืœื”ืฉืชื ื•ืช ื›ื›ืœ ืฉื”ืžืชื–ืžืŸ ื ืขืฉื” ื—ื›ื ื™ื•ืชืจ, ืื‘ืœ ืขื‘ื•ืจ 9.4/9.6 ื–ื” ื ืจืื” ื‘ืขืจืš ืื•ืชื• ื”ื“ื‘ืจ, ื›ืžื• ื‘ื“ื•ื’ืžืื•ืช ื›ืืŸ.

ื‘ื•ืื• ื ื™ืงื— ื‘ืงืฉื” ืืžื™ืชื™ืช ืžืื•ื“:

SELECT
  TRUE
FROM
  "ะ”ะพะบัƒะผะตะฝั‚" d
INNER JOIN
  "ะ”ะพะบัƒะผะตะฝั‚ะ ะฐััˆะธั€ะตะฝะธะต" doc_ex
    USING("@ะ”ะพะบัƒะผะตะฝั‚")
INNER JOIN
  "ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ" t_doc ON
    t_doc."@ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ" = d."ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ"
WHERE
  (d."ะ›ะธั†ะพ3" = 19091 or d."ะกะพั‚ั€ัƒะดะฝะธะบ" = 19091) AND
  d."$ะงะตั€ะฝะพะฒะธะบ" IS NULL AND
  d."ะฃะดะฐะปะตะฝ" IS NOT TRUE AND
  doc_ex."ะกะพัั‚ะพัะฝะธะต"[1] IS TRUE AND
  t_doc."ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ" = 'ะŸะปะฐะฝะ ะฐะฑะพั‚'
LIMIT 1;

ืœื’ื‘ื™ ืฉืžื•ืช ื˜ื‘ืœืื•ืช ื•ืฉื“ื•ืชื ื™ืชืŸ ืœื”ืชื™ื™ื—ืก ืื—ืจืช ืœืฉืžื•ืช ื”"ืจื•ืกื™ื" ืฉืœ ืฉื“ื•ืช ื•ื˜ื‘ืœืื•ืช, ืื‘ืœ ื–ื” ืขื ื™ื™ืŸ ืฉืœ ื˜ืขื. ื‘ื’ืœืœ ื” ื›ืืŸ ื‘ื˜ื ืกื•ืจ ืื™ืŸ ืžืคืชื—ื™ื ื–ืจื™ื, ื•-PostgreSQL ืžืืคืฉืจ ืœื ื• ืœืชืช ืฉืžื•ืช ืืคื™ืœื• ื‘ื”ื™ืจื•ื’ืœื™ืคื™ื, ืื ื”ื ืžื•ืงืฃ ื‘ืžืจื›ืื•ืช, ืื– ืื ื—ื ื• ืžืขื“ื™ืคื™ื ืœืชืช ืฉื ืœืื•ื‘ื™ื™ืงื˜ื™ื ื‘ืฆื•ืจื” ื—ื“ ืžืฉืžืขื™ืช ื•ื‘ืจื•ืจื” ื›ื“ื™ ืฉืœื ื™ื”ื™ื• ืื™ ื”ืชืืžื•ืช.
ื‘ื•ืื• ื ืกืชื›ืœ ืขืœ ื”ืชื•ื›ื ื™ืช ืฉื”ืชืงื‘ืœื”:
PostgreSQL Antipatterns: JOINs ื•-ORs ืžื–ื™ืงื™ื
[ื”ืกืชื›ืœ ื‘-explain.tensor.ru]

ืžืื’ืจื™ื ืฉืœ 144ms ื•ื›ืžืขื˜ 53K - ื›ืœื•ืžืจ, ื™ื•ืชืจ ืž-400MB ืฉืœ ื ืชื•ื ื™ื! ื•ื™ื”ื™ื” ืœื ื• ืžื–ืœ ืื ื›ื•ืœื ื™ื”ื™ื• ื‘ืžื˜ืžื•ืŸ ืขื“ ืœื‘ืงืฉืชื ื•, ืื—ืจืช ื–ื” ื™ื™ืงื— ื”ืจื‘ื” ื™ื•ืชืจ ื–ืžืŸ ื‘ืงืจื™ืื” ืžื”ื“ื™ืกืง.

ื”ืืœื’ื•ืจื™ืชื ื”ื›ื™ ื—ืฉื•ื‘!

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

ืื– ื”ื‘ืงืฉื”:
- ื‘ื•ื“ืง ืืช ืงื™ื•ืžื• ืฉืœ ืžืกืžืš ื›ืœืฉื”ื• ืœืคื—ื•ืช
- ื‘ืžืฆื‘ ืฉืื ื• ืฆืจื™ื›ื™ื ื•ืžืกื•ื’ ืžืกื•ื™ื
- ื›ืืฉืจ ื”ืžื—ื‘ืจ ืื• ื”ืžื‘ืฆืข ื”ื•ื ื”ืขื•ื‘ื“ ืฉืื ื• ืฆืจื™ื›ื™ื

JOIN + LIMIT 1

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

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

WITH T AS (
  SELECT
    "@ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ"
  FROM
    "ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ"
  WHERE
    "ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ" = 'ะŸะปะฐะฝะ ะฐะฑะพั‚'
  LIMIT 1
)
...
WHERE
  d."ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ" = (TABLE T)
...

ื›ืŸ, ืื ื”ื˜ื‘ืœื”/CTE ืžื•ืจื›ื‘ืช ืžืฉื“ื” ื‘ื•ื“ื“ ืฉืœ ืจืฉื•ืžื” ื‘ื•ื“ื“ืช, ืื– ื‘-PG ืืคืฉืจ ืืคื™ืœื• ืœื›ืชื•ื‘ ื›ืš, ื‘ืžืงื•ื

d."ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ" = (SELECT "@ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ" FROM T LIMIT 1)

ื”ืขืจื›ื” ืขืฆืœื ื™ืช ื‘ืฉืื™ืœืชื•ืช PostgreSQL

BitmapOr ื ื’ื“ UNION

ื‘ืžืงืจื™ื ืžืกื•ื™ืžื™ื, Bitmap Heap Scan ื™ืขืœื” ืœื ื• ื”ืจื‘ื” โ€“ ืœืžืฉืœ ื‘ืžืฆื‘ ืฉืœื ื•, ื›ืืฉืจ ืœื ืžืขื˜ ืจืฉื•ืžื•ืช ืขื•ืžื“ื•ืช ื‘ืชื ืื™ ื”ื ื“ืจืฉ. ืงื™ื‘ืœื ื• ืืช ื–ื” ื‘ื’ืœืœ ืžืฆื‘ OR ื”ืคืš ืœ- BitmapOr- ืชืคืขื•ืœ ื‘ืชื•ื›ื ื™ืช.
ื ื—ื–ื•ืจ ืœื‘ืขื™ื” ื”ืžืงื•ืจื™ืช - ืขืœื™ื ื• ืœืžืฆื•ื ืจืฉื•ืžื” ืžืชืื™ืžื” ืœื›ืœ ืื—ื“ ืžื”ืชื ืื™ื - ื›ืœื•ืžืจ, ืื™ืŸ ืฆื•ืจืš ืœื—ืคืฉ ืืช ื›ืœ ืจืฉื•ืžื•ืช 59K ื‘ืฉื ื™ ื”ืชื ืื™ื. ื™ืฉ ื“ืจืš ืœืคืชื•ืจ ืชื ืื™ ืื—ื“, ื• ืขื‘ื•ืจ ืืœ ื”ืฉื ื™ ืจืง ื›ืืฉืจ ื“ื‘ืจ ืœื ื ืžืฆื ื‘ืจืืฉื•ืŸ. ื”ืขื™ืฆื•ื‘ ื”ื‘ื ื™ืขื–ื•ืจ ืœื ื•:

(
  SELECT
    ...
  LIMIT 1
)
UNION ALL
(
  SELECT
    ...
  LIMIT 1
)
LIMIT 1

"ื—ื™ืฆื•ื ื™" LIMIT 1 ืžื‘ื˜ื™ื—ื” ืฉื”ื—ื™ืคื•ืฉ ื™ืกืชื™ื™ื ื›ืืฉืจ ื”ืจืฉื•ืžื” ื”ืจืืฉื•ื ื” ื ืžืฆืื”. ื•ืื ื”ื•ื ื›ื‘ืจ ื ืžืฆื ื‘ื‘ืœื•ืง ื”ืจืืฉื•ืŸ, ื”ื‘ืœื•ืง ื”ืฉื ื™ ืœื ื™ืชื‘ืฆืข (ืžืขื•ืœื ืœื ื”ื•ืฆื ืœื”ื•ืจื’ ืœื’ื‘ื™).

"ื”ืกืชืจืช ืชื ืื™ื ืงืฉื™ื ืชื—ืช CASE"

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

SELECT
  ...
FROM
  "ะ”ะพะบัƒะผะตะฝั‚" d
WHERE
  ... /*index cond*/ AND
  CASE
    WHEN "$ะงะตั€ะฝะพะฒะธะบ" IS NULL AND "ะฃะดะฐะปะตะฝ" IS NOT TRUE THEN (
      SELECT
        "ะกะพัั‚ะพัะฝะธะต"[1] IS TRUE
      FROM
        "ะ”ะพะบัƒะผะตะฝั‚ะ ะฐััˆะธั€ะตะฝะธะต"
      WHERE
        "@ะ”ะพะบัƒะผะตะฝั‚" = d."@ะ”ะพะบัƒะผะตะฝั‚"
    )
  END

ืคืขื ืื—ืช ืžื”ื˜ื‘ืœื” ื”ืžืงื•ืฉืจืช ืืœื™ื ื• ืื™ืŸ ืฆื•ืจืš ื‘ืืฃ ืื—ื“ ืžื”ืฉื“ื•ืช ืœืชื•ืฆืื”, ืื– ื™ืฉ ืœื ื• ื”ื–ื“ืžื ื•ืช ืœื”ืคื•ืš ืืช JOIN ืœืชื ืื™ ื‘ืฉืื™ืœืชืช ืžืฉื ื”.
ื”ื‘ื” ื ืฉืื™ืจ ืืช ื”ืฉื“ื•ืช ืฉืฆื•ืจืคื• ืœืื™ื ื“ืงืก "ืžื—ื•ืฅ ืœืกื•ื’ืจื™ื™ื ืฉืœ CASE", ื ื•ืกื™ืฃ ืชื ืื™ื ืคืฉื•ื˜ื™ื ืžื”ืจืฉื•ืžื” ืœื‘ืœื•ืง WHEN - ื•ืขื›ืฉื™ื• ื”ืฉืื™ืœืชื” ื”"ื›ื‘ื“ื”" ืžื‘ื•ืฆืขืช ืจืง ื‘ืขืช ืžืขื‘ืจ ืœ-THEN.

ืฉื ื”ืžืฉืคื—ื” ืฉืœื™ ื”ื•ื "ืกื”"ื›"

ืื ื• ืื•ืกืคื™ื ืืช ื”ืฉืื™ืœืชื” ื”ืžืชืงื‘ืœืช ืขื ื›ืœ ื”ืžื›ื ื™ืงื” ืฉืชื•ืืจื” ืœืขื™ืœ:

WITH T AS (
  SELECT
    "@ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ"
  FROM
    "ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ"
  WHERE
    "ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ" = 'ะŸะปะฐะฝะ ะฐะฑะพั‚'
)
  (
    SELECT
      TRUE
    FROM
      "ะ”ะพะบัƒะผะตะฝั‚" d
    WHERE
      ("ะ›ะธั†ะพ3", "ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ") = (19091, (TABLE T)) AND
      CASE
        WHEN "$ะงะตั€ะฝะพะฒะธะบ" IS NULL AND "ะฃะดะฐะปะตะฝ" IS NOT TRUE THEN (
          SELECT
            "ะกะพัั‚ะพัะฝะธะต"[1] IS TRUE
          FROM
            "ะ”ะพะบัƒะผะตะฝั‚ะ ะฐััˆะธั€ะตะฝะธะต"
          WHERE
            "@ะ”ะพะบัƒะผะตะฝั‚" = d."@ะ”ะพะบัƒะผะตะฝั‚"
        )
      END
    LIMIT 1
  )
UNION ALL
  (
    SELECT
      TRUE
    FROM
      "ะ”ะพะบัƒะผะตะฝั‚" d
    WHERE
      ("ะขะธะฟะ”ะพะบัƒะผะตะฝั‚ะฐ", "ะกะพั‚ั€ัƒะดะฝะธะบ") = ((TABLE T), 19091) AND
      CASE
        WHEN "$ะงะตั€ะฝะพะฒะธะบ" IS NULL AND "ะฃะดะฐะปะตะฝ" IS NOT TRUE THEN (
          SELECT
            "ะกะพัั‚ะพัะฝะธะต"[1] IS TRUE
          FROM
            "ะ”ะพะบัƒะผะตะฝั‚ะ ะฐััˆะธั€ะตะฝะธะต"
          WHERE
            "@ะ”ะพะบัƒะผะตะฝั‚" = d."@ะ”ะพะบัƒะผะตะฝั‚"
        )
      END
    LIMIT 1
  )
LIMIT 1;

ื”ืชืืžืช [ืœ] ืื™ื ื“ืงืกื™ื

ืขื™ืŸ ืžืื•ืžื ืช ื”ื‘ื—ื™ื ื” ืฉืชื ืื™ ื”ืื™ื ื“ืงืก ื‘ืชืชื™ ื‘ืœื•ืงื™ื ืฉืœ UNION ืฉื•ื ื™ื ื‘ืžืงืฆืช - ื–ื” ื‘ื’ืœืœ ืฉื›ื‘ืจ ื™ืฉ ืœื ื• ืื™ื ื“ืงืกื™ื ืžืชืื™ืžื™ื ืขืœ ื”ืฉื•ืœื—ืŸ. ื•ืื ื”ื ืœื ื”ื™ื• ืงื™ื™ืžื™ื, ื›ื“ืื™ ืœื™ืฆื•ืจ: Document(Person3, DocumentType) ะธ ืžืกืžืš (DocumentType, Employee).
ืขืœ ืกื“ืจ ื”ืฉื“ื•ืช ื‘ืชื ืื™ ROWืžื ืงื•ื“ืช ืžื‘ื˜ื• ืฉืœ ื”ืžืชื›ื ืŸ ืืคืฉืจ ื›ืžื•ื‘ืŸ ืœื›ืชื•ื‘ (A, B) = (constA, constB)ื• - (B, A) = (constB, constA). ืื‘ืœ ื‘ื–ืžืŸ ื”ื”ืงืœื˜ื” ืœืคื™ ืกื“ืจ ื”ืฉื“ื•ืช ื‘ืื™ื ื“ืงืก, ื‘ืงืฉื” ื›ื–ื• ืคืฉื•ื˜ ื ื•ื—ื” ื™ื•ืชืจ ืœื ื™ืคื•ื™ ื‘ืื’ื™ื ืžืื•ื—ืจ ื™ื•ืชืจ.
ืžื” ื‘ืชื•ื›ื ื™ืช?
PostgreSQL Antipatterns: JOINs ื•-ORs ืžื–ื™ืงื™ื
[ื”ืกืชื›ืœ ื‘-explain.tensor.ru]

ืœืจื•ืข ื”ืžื–ืœ, ื”ืชืžื–ืœ ืžื–ืœื ื• ื•ืœื ื ืžืฆื ื“ื‘ืจ ื‘ื‘ืœื•ืง UNION ื”ืจืืฉื•ืŸ, ื›ืš ืฉื”ืฉื ื™ ืขื“ื™ื™ืŸ ื”ื•ืฆื ืœื”ื•ืจื’. ืื‘ืœ ื’ื ื›ืš - ื‘ืœื‘ื“ 0.037ms ื•-11 ืžืื’ืจื™ื!
ื–ื™ืจื–ื ื• ืืช ื”ื‘ืงืฉื” ื•ืฆืžืฆืžื ื• ืืช ืฉืื™ื‘ืช ื”ื ืชื•ื ื™ื ื‘ื–ื™ื›ืจื•ืŸ ื›ืžื” ืืœืคื™ ืคืขืžื™ื, ื‘ืืžืฆืขื•ืช ื˜ื›ื ื™ืงื•ืช ืคืฉื•ื˜ื•ืช ืœืžื“ื™ - ืชื•ืฆืื” ื˜ื•ื‘ื” ืขื ืžืขื˜ ื”ืขืชืง-ื”ื“ื‘ืง. ๐Ÿ™‚

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

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