PostgreSQL ์•ˆํ‹ฐํŒจํ„ด: "ํ•˜๋‚˜๋งŒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!"

SQL์—์„œ๋Š” ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” "๋ฐฉ๋ฒ•"์ด ์•„๋‹ˆ๋ผ ๋‹ฌ์„ฑํ•˜๋ ค๋Š” "๋ฌด์—‡"์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ "๋“ค์€ ๋Œ€๋กœ ์“ฐ์—ฌ์ง„๋‹ค"๋ผ๋Š” ์Šคํƒ€์ผ๋กœ SQL ์ฟผ๋ฆฌ๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๊ทธ ์ž๋ฆฌ๋ฅผ ๋Œ€์‹ ํ•ฉ๋‹ˆ๋‹ค. SQL์˜ ๊ณ„์‚ฐ ์กฐ๊ฑด ๊ธฐ๋Šฅ.

์˜ค๋Š˜์€ ๋งค์šฐ ๊ฐ„๋‹จํ•œ ์˜ˆ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๊ฒƒ์ด ์‚ฌ์šฉ ๋งฅ๋ฝ์—์„œ ์–ด๋–ค ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. GROUP/DISTINCT ะธ LIMIT ๊ทธ๋“ค๊ณผ ํ•จ๊ป˜.

์ด์ œ ์š”์ฒญ์— ์ผ๋‹ค๋ฉด โ€œ๋จผ์ € ์ด ํ‘œ์ง€ํŒ๋“ค์„ ์—ฐ๊ฒฐํ•œ ๋‹ค์Œ, ์ค‘๋ณต๋œ ๊ฒƒ๋“ค์„ ๋ชจ๋‘ ๋ฒ„๋ฆฌ์„ธ์š”. ํ•˜๋‚˜๋งŒ ๋‚จ์•„์•ผ ํ•ด ๊ฐ ํ‚ค์— ๋Œ€ํ•ด ๋ณต์‚ฌ" - ์—ฐ๊ฒฐ์ด ์ „ํ˜€ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋„ ์ด๊ฒƒ์ด ์ •ํ™•ํžˆ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

๋•Œ๋กœ๋Š” ์šด์ด ์ข‹์•„์„œ "๊ทธ๋ƒฅ ์ž‘๋™"ํ•˜๊ธฐ๋„ ํ•˜๊ณ , ๋•Œ๋กœ๋Š” ์„ฑ๋Šฅ์— ๋ถˆ์พŒํ•œ ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ธฐ๋„ ํ•˜๊ณ , ๋•Œ๋กœ๋Š” ๊ฐœ๋ฐœ์ž์˜ ๊ด€์ ์—์„œ ์ „ํ˜€ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํšจ๊ณผ๋ฅผ ์ฃผ๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

PostgreSQL ์•ˆํ‹ฐํŒจํ„ด: "ํ•˜๋‚˜๋งŒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!"
๊ธ€์Ž„์š”, ๊ทธ๋ ‡๊ฒŒ ํ›Œ๋ฅญํ•˜์ง€๋Š” ์•Š์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ...

โ€œ๋‹ฌ์ฝคํ•œ ์ปคํ”Œโ€: JOIN + DISTINCT

SELECT DISTINCT
  X.*
FROM
  X
JOIN
  Y
    ON Y.fk = X.pk
WHERE
  Y.bool_condition;

๊ทธ๋“ค์ด ์›ํ•˜๋Š” ๊ฒƒ์ด ๋ฌด์—‡์ธ์ง€๋Š” ๋ถ„๋ช…ํ•  ๊ฒƒ์ด๋‹ค. ์ถฉ์กฑ๋œ ์กฐ๊ฑด๊ณผ ๊ด€๋ จ๋œ Y์˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” ๋ ˆ์ฝ”๋“œ X๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.. ๋‹ค์Œ์„ ํ†ตํ•ด ์š”์ฒญ์„ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. JOIN โ€” pk ๊ฐ’์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค(์ •ํ™•ํžˆ Y์— ์ ํ•ฉํ•œ ํ•ญ๋ชฉ์ด ๋ช‡ ๊ฐœ๋‚˜ ๋‚˜ํƒ€๋‚ฌ๋Š”์ง€). ์ œ๊ฑฐํ•˜๋Š” ๋ฐฉ๋ฒ•? ํ‹€๋ฆผ์—†์ด DISTINCT!

๊ฐ X ๋ ˆ์ฝ”๋“œ์— ๋Œ€ํ•ด ์ˆ˜๋ฐฑ ๊ฐœ์˜ ๊ด€๋ จ Y ๋ ˆ์ฝ”๋“œ๊ฐ€ ์žˆ๊ณ  ์ค‘๋ณต ํ•ญ๋ชฉ์ด ์˜์›…์ ์œผ๋กœ ์ œ๊ฑฐ๋˜๋ฉด ํŠนํžˆ "๊ธฐ์˜๋‹ค".

PostgreSQL ์•ˆํ‹ฐํŒจํ„ด: "ํ•˜๋‚˜๋งŒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!"

์–ด๋–ป๊ฒŒ ๊ณ ์น˜๋Š” ์ง€? ์šฐ์„  ๋ฌธ์ œ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์ธ์‹ํ•˜์„ธ์š”. "Y์— ์ถฉ์กฑ๋œ ์กฐ๊ฑด๊ณผ ์—ฐ๊ด€๋œ AT LEAST ONE์ด ์žˆ๋Š” X ๋ ˆ์ฝ”๋“œ๋ฅผ ์„ ํƒํ•˜์„ธ์š”." -๊ฒฐ๊ตญ Y ๋ ˆ์ฝ”๋“œ ์ž์ฒด์—์„œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ค‘์ฒฉ๋œ ์กด์žฌ

SELECT
  *
FROM
  X
WHERE
  EXISTS(
    SELECT
      NULL
    FROM
      Y
    WHERE
      fk = X.pk AND
      bool_condition
    LIMIT 1
  );

์ผ๋ถ€ PostgreSQL ๋ฒ„์ „์€ EXISTS์—์„œ๋Š” ๋‚˜ํƒ€๋‚˜๋Š” ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ์„ ์ฐพ๋Š” ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ ์ด์ „ ๋ฒ„์ „์€ ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๋‚˜๋Š” ํ•ญ์ƒ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค LIMIT 1 ์ด๋‚ด EXISTS.

์ธก๋ฉด ์กฐ์ธ

SELECT
  X.*
FROM
  X
, LATERAL (
    SELECT
      Y.*
    FROM
      Y
    WHERE
      fk = X.pk AND
      bool_condition
    LIMIT 1
  ) Y
WHERE
  Y IS DISTINCT FROM NULL;

๋™์ผํ•œ ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ฐœ๊ฒฌ๋œ ๊ด€๋ จ Y ๋ ˆ์ฝ”๋“œ์—์„œ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ์‚ฌํ•œ ์˜ต์…˜์ด ๊ธฐ์‚ฌ์—์„œ ๋…ผ์˜๋ฉ๋‹ˆ๋‹ค. "PostgreSQL ์•ˆํ‹ฐํŒจํ„ด: ๋“œ๋ฌธ ๋ ˆ์ฝ”๋“œ๊ฐ€ JOIN ์ค‘๊ฐ„์— ๋„๋‹ฌํ•ฉ๋‹ˆ๋‹ค.".

โ€œ๋” ๋งŽ์€ ๋น„์šฉ์„ ์ง€๋ถˆํ•ด์•ผ ํ•˜๋Š” ์ด์œ โ€: DISTINCT [ON] + LIMIT 1

์ด๋Ÿฌํ•œ ์ฟผ๋ฆฌ ๋ณ€ํ™˜์˜ ๋˜ ๋‹ค๋ฅธ ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ ˆ์ฝ”๋“œ ์ค‘ ํ•˜๋‚˜ ๋˜๋Š” ๋ช‡ ๊ฐœ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ ˆ์ฝ”๋“œ ๊ฒ€์ƒ‰์„ ์‰ฝ๊ฒŒ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

SELECT DISTINCT ON(X.pk)
  *
FROM
  X
JOIN
  Y
    ON Y.fk = X.pk
LIMIT 1;

์ด์ œ ์š”์ฒญ์„ ์ฝ๊ณ  DBMS๊ฐ€ ์ˆ˜ํ–‰ํ•  ์ž‘์—…์ด ๋ฌด์—‡์ธ์ง€ ์ดํ•ดํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

  • ํ‘œ์ง€ํŒ์„ ์—ฐ๊ฒฐํ•˜๋‹ค
  • X.pk ๊ณ ์œ 
  • ๋‚˜๋จธ์ง€ ํ•ญ๋ชฉ ์ค‘์—์„œ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜์„ธ์š”.

๊ทธ๋ž˜์„œ ๋ฌด์—‡์„ ์–ป์—ˆ๋‚˜์š”? "ํ•œ ๋ฒˆ๋งŒ ์ž…๋ ฅํ•˜์„ธ์š”" ๊ณ ์œ ํ•œ ๊ฒƒ ์ค‘์—์„œ - ๊ณ ์œ ํ•˜์ง€ ์•Š์€ ๊ฒƒ์„ ์ทจํ•˜๋ฉด ๊ฒฐ๊ณผ๊ฐ€ ์–ด๋–ป๊ฒŒ๋“  ๋ฐ”๋€”๊นŒ์š”?.. "์ฐจ์ด๊ฐ€ ์—†๋‹ค๋ฉด ์™œ ๋” ๋งŽ์€ ๋น„์šฉ์„ ์ง€๋ถˆํ•ฉ๋‹ˆ๊นŒ?"

SELECT
  *
FROM
  (
    SELECT
      *
    FROM
      X
    -- ััŽะดะฐ ะผะพะถะฝะพ ะฟะพะดััƒะฝัƒั‚ัŒ ะฟะพะดั…ะพะดัั‰ะธั… ัƒัะปะพะฒะธะน
    LIMIT 1 -- +1 Limit
  ) X
JOIN
  Y
    ON Y.fk = X.pk
LIMIT 1;

๊ทธ๋ฆฌ๊ณ  ์ •ํ™•ํžˆ ๊ฐ™์€ ์ฃผ์ œ GROUP BY + LIMIT 1.

"๋ฌผ์–ด๋ณผ ๊ฒŒ ์žˆ์–ด์š”": ์•”์‹œ์  GROUP + LIMIT

๋น„์Šทํ•œ ์ผ์ด ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋น„์–ด ์žˆ์ง€ ์•Š์€์ง€ ํ™•์ธ ์š”์ฒญ์ด ์ง„ํ–‰๋จ์— ๋”ฐ๋ผ ์„œ๋ช… ๋˜๋Š” CTE:

...
CASE
  WHEN (
    SELECT
      count(*)
    FROM
      X
    LIMIT 1
  ) = 0 THEN ...

์ง‘๊ณ„ ํ•จ์ˆ˜(count/min/max/sum/...)์€ ๋ช…์‹œ์ ์ธ ์ง€์นจ ์—†์ด๋„ ์ „์ฒด ์„ธํŠธ์—์„œ ์„ฑ๊ณต์ ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. GROUP BY. ์˜ค์ง LIMIT ๊ทธ๋“ค์€ ๊ทธ๋‹ค์ง€ ์นœ์ ˆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์ž๋Š” ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค โ€œ๊ฑฐ๊ธฐ์— ๊ธฐ๋ก์ด ์žˆ๋‹ค๋ฉด LIMIT๋งŒ ์žˆ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.โ€. ํ•˜์ง€๋งŒ ๊ทธ๋Ÿฌ์ง€ ๋งˆ์„ธ์š”! ๋ฒ ์ด์Šค์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๊ทธ๋“ค์ด ์›ํ•˜๋Š” ๊ฒƒ์„ ์„ธ์–ด๋ณด์„ธ์š” ๋ชจ๋“  ๊ธฐ๋ก์— ์˜ํ•˜๋ฉด
  • ๊ทธ๋“ค์ด ์š”๊ตฌํ•˜๋Š” ๋งŒํผ ๋งŽ์€ ๋Œ€์‚ฌ๋ฅผ ์ค˜

๋Œ€์ƒ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋‹ค์Œ ๋Œ€์ฒด ์ค‘ ํ•˜๋‚˜๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

  • (count + LIMIT 1) = 0 ์— NOT EXISTS(LIMIT 1)
  • (count + LIMIT 1) > 0 ์— EXISTS(LIMIT 1)
  • count >= N ์— (SELECT count(*) FROM (... LIMIT N))

"๊ทธ๋žจ ๋‹จ์œ„๋กœ ๋งค๋‹ฌ์•„์•ผ ํ•  ์–‘": DISTINCT + LIMIT

SELECT DISTINCT
  pk
FROM
  X
LIMIT $1

์ˆœ์ง„ํ•œ ๊ฐœ๋ฐœ์ž๋Š” ์š”์ฒญ ์‹คํ–‰์ด ์ค‘์ง€๋  ๊ฒƒ์ด๋ผ๊ณ  ์ง„์‹ฌ์œผ๋กœ ๋ฏฟ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์œผ๋กœ ๋ฐœ๊ฒฌ๋œ ๋‹ค๋ฅธ ๊ฐ’ ์ค‘ 1๋‹ฌ๋Ÿฌ๋ฅผ ์ฐพ์ž๋งˆ์ž.

๋ฏธ๋ž˜์—๋Š” ์ƒˆ๋กœ์šด ๋…ธ๋“œ ๋•๋ถ„์— ์ด๊ฒƒ์ด ์ž‘๋™ํ•  ์ˆ˜๋„ ์žˆ๊ณ  ์ž‘๋™ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ธ๋ฑ์Šค ๊ฑด๋„ˆ๋›ฐ๊ธฐ ์Šค์บ”, ๊ตฌํ˜„์ด ํ˜„์žฌ ์ง„ํ–‰ ์ค‘์ด์ง€๋งŒ ์•„์ง์€ ์•„๋‹™๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ ๋จผ์ € ๋ชจ๋“  ๊ธฐ๋ก์ด ๊ฒ€์ƒ‰๋ฉ๋‹ˆ๋‹ค, ๊ณ ์œ ํ•˜๋ฉฐ ํ•ด๋‹น ์ค‘์—์„œ๋งŒ ์š”์ฒญํ•œ ๊ธˆ์•ก์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ์›ํ–ˆ๋‹ค๋ฉด ํŠนํžˆ ์Šฌํ”„๋‹ค $ 1 = 4, ํ…Œ์ด๋ธ”์—๋Š” ์ˆ˜์‹ญ๋งŒ ๊ฐœ์˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šฌํ””์„ ํ—›๋˜์ด ๋ณด๋‚ด์ง€ ์•Š๊ธฐ ์œ„ํ•ด ์žฌ๊ท€ ์ฟผ๋ฆฌ๋ฅผ ํ™œ์šฉํ•ด๋ณด์ž PostgreSQL Wiki์˜ "DISTINCT๋Š” ๊ฐ€๋‚œํ•œ ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.":

PostgreSQL ์•ˆํ‹ฐํŒจํ„ด: "ํ•˜๋‚˜๋งŒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!"

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€