[PostgreSQL] Partial Index

Partial ์ธ๋ฑ์Šค๋Š” PostgreSQL์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ์ธ๋ฑ์Šค์˜ ํ™•์žฅ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋‹ค.

์ธ๋ฑ์Šค์— ๋Œ€ํ•ด์„œ ํŠน์ • ์กฐ๊ฑด์„ ๋ถ€์—ฌํ•ด์„œ, ์กฐ๊ฑด์— ๋ถ€ํ•ฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ์ธ๋ฑ์Šค์— ํฌํ•จ๋˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
๊ทธ๋ž˜์„œ ๋ถ€๋ถ„(Partial) ์ธ๋ฑ์Šค๋‹ค.

Create Index ์ธ๋ฑ์Šค๋ช… on ํ…Œ์ด๋ธ”๋ช…(์ปฌ๋Ÿผ...) where ์กฐ๊ฑด์‹

์ € ์กฐ๊ฑด์‹์— ํฌํ•จ๋˜๋Š” Query๋Š” ์ธ๋ฑ์Šค์— ๊ฑธ๋ ค์„œ ์ž˜ ์ธ๋ฑ์‹ฑ๋˜๊ณ , ํฌํ•จ๋˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋Š” ์ธ๋ฑ์‹ฑ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค.

false์ธ ๋ฐ์ดํ„ฐ๋Š” ์• ์ดˆ๋ถ€ํ„ฐ ์ธ๋ฑ์Šค ํŠธ๋ฆฌ์— ์ €์žฅ์„ ํ•˜์ง€๋„ ์•Š๊ณ , ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— SELECT ์ฟผ๋ฆฌ ํ”Œ๋žœ ๋„์ค‘์— ์กฐ๊ฑด์— ๊ฑธ๋ ค์žˆ์ง€ ์•Š๋‹ค๋ฉด ์ธ๋ฑ์Šค ์Šค์บ”์„ ์‹œ๋„ํ•˜์ง€๋„ ์•Š๋Š” ๊ฒƒ์ด๋‹ค.

์ด ๊ธฐ๋Šฅ์˜ ์žฅ์ ์€, ์กฐ์ •ํ•˜๊ธฐ์— ๋”ฐ๋ผ์„œ ์ธ๋ฑ์Šค ์ €์žฅ๊ณต๊ฐ„์„ ๋” ์•„๋‚„ ์ˆ˜ ์žˆ๊ณ , Update ์‹œ์˜ ์ธ๋ฑ์Šค ์ˆ˜์ • ๋น„์šฉ๋„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
๊ทผ๋ฐ ์•„๋งˆ ์ด ์ด์œ ๋งŒ์œผ๋กœ ์“ฐ๋Š” ์‚ฌ๋žŒ์€ ๋ช‡ ์—†์„ ๊ฒƒ์ด๋‹ค.




์‘์šฉ: Partial Unique Index

์‚ฌ์‹ค ์ด ๊ธฐ๋Šฅ์˜ ๊ฐ€์žฅ ์œ ์šฉํ•œ ์‘์šฉ ๋ฐฉ๋ฒ•์€, ์กฐ๊ฑด์— ๋”ฐ๋ฅธ ์„ ํƒ์  Unique๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ด๋ฉ”์ผ์„ ์œ ์ผํ‚ค๋กœ ์žก๋Š” ๊ณ„์ • ์ •๋ณด ํ…Œ์ด๋ธ”์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด๊ฒ ๋‹ค.

Create Table "user" (
    id SERIAL PRIMARY KEY,
    email VARCHAR(100) NOT NULL,
    password VARCHAR(200) NOT NULL,
    name VARCHAR(100) NOT NULL,
    leftAt TIMESTAMPTZ -- ํƒˆํ‡ด ์‹œ์ 
);

INSERT INTO "user" (email, password, name, leftAt) VALUES
('asdf@example.com', 'q1w2e3r4', 'John', NULL),
('qert@example.com', 'q1w2e3r4', 'Tom', NULL),
('foo.bar@example.com', 'q1w2e3r4', 'Anna', NULL),
('buldak@example.com', 'q1w2e3r4', 'Alice Four', NULL),
('gang@example.com', 'q1w2e3r4', 'jonathan', NULL),
('kimchi@example.com', 'q1w2e3r4', 'kim', NULL),
('do@example.com', 'q1w2e3r4', 'doe', NULL);

๋ป”ํ•œ ๊ตฌ์„ฑ์ด๋‹ค.
์ด๋ฉ”์ผ์ด ์žˆ๊ณ , ํƒˆํ‡ด ์‹œ์ ์„ ๊ธฐ๋กํ•˜๋Š” leftat ํ•„๋“œ๋„ ์žˆ๋‹ค.
์ €๊ฒŒ null์ด ์•„๋‹ˆ๋ฉด ํƒˆํ‡ดํ•œ ํšŒ์›์ธ ๊ฒƒ์ด๋‹ค.

์—ฌ๊ธฐ์— ๋Œ€ํ•ด์„œ ์ด๋ฉ”์ผ ์ œ์•ฝ์„ ๊ฑด๋‹ค๋ฉด, ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๋ฉด์„œ๋„ ๊ฐ•๋ ฅํ•œ ๋ฐฉ๋ฒ•์€ Unique ์ธ๋ฑ์Šค๋ฅผ ๊ฝ‚์•„๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ค.

Create Unique Index email_unique on "user"(email);

INSERT INTO "user" (email, password, name, leftAt) VALUES
('asdf@example.com', 'q1w2e3r4', 'Other People', NULL);

์„œ๋ฒ„์—๋‹ค๊ฐ€ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉด ๋™์‹œ์„ฑ ๋ฌธ์ œ๋กœ ์œ ์ผ์„ฑ์ด ๊นจ์งˆ ํ™•๋ฅ ๋„ ๋†’๊ณ , ๊ฐ•๋ ฅํ•œ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜๋ ค๋ฉด ๊ฒฐ๊ตญ์—๋Š” ๋‹จ์ผ ๋ฝ์ด ํ•„์š”ํ•˜๋‹ค.
๊ทผ๋ฐ RDB ์ž์ฒด๊ฐ€ ๊ฐ•๋ ฅํ•œ ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๋‹จ์ผ ์Šคํ† ๋ฆฌ์ง€์ด๋‹ˆ, ์œ ์ผ์„ฑ ๋ณด์žฅ์— ์ด๋งŒํผ ํšจ์œจ์ ์ด๊ณ  ์™„๋ฒฝํ•œ ์†”๋ฃจ์…˜์€ ๊ฑฐ์˜ ์—†๋‹ค๊ณ  ๋ด๋„ ๋  ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ ์ข€ ์‘์šฉ์„ ํ•˜๋‹ค๋ณด๋ฉด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

update "user"
set leftAt = NOW()
where email = 'asdf@example.com';

์˜ˆ๋ฅผ ๋“ค๋ฉด, ํƒˆํ‡ดํ•œ ํšŒ์›์ด ์žˆ์„๋•Œ, ๋™์ผํ•œ ์ด๋ฉ”์ผ๋กœ ์žฌ๊ฐ€์ž…์„ ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.
๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?
email+leftat์„ ๋ฌถ์–ด์„œ Unique๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ํ•ด๋„, null์ด ํฌํ•จ๋˜๋ฉด Unique ์ œ์•ฝ์—์„œ ๋ฒ—์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ์š”๊ตฌ์‚ฌํ•ญ์„ ๋งŒ์กฑํ•  ์ˆ˜ ์—†๋‹ค.

์—ฌ๊ธฐ์— ๋Œ€ํ•ด์„œ ๊ฐ€์žฅ ๋ช…์พŒํ•œ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์€ Partial Unique ์ธ๋ฑ์Šค๋กœ ๋ถ€๋ถ„์ ์ธ ์ œ์•ฝ์„ ๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
์˜ˆ๋ฅผ ๋“ค๋ฉด, ์ด๋Ÿฐ ์‹์ด๋‹ค.

Drop Index email_unique;
Create Unique Index email_unique on "user"(email) where leftAt IS NULL;

์ด๋Ÿฌ๋ฉด leftAt IS NULL์„ ๋งŒ์กฑํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด์„œ๋งŒ email ์œ ์ผ์„ฑ์„ ๋ณด์žฅํ•˜๋„๋ก ์ธ๋ฑ์Šค๊ฐ€ ๊ตฌ์„ฑ๋œ๋‹ค.


INSERT INTO "user" (email, password, name, leftAt) VALUES
('asdf@example.com', 'q1w2e3r4', 'Other People', NULL);

๊ทธ๋Ÿฌ๋ฉด leftat=null์ด ์•„๋‹Œ ๋ฐ์ดํ„ฐ๋Š” ์œ ์ผ์„ฑ ์ œ์•ฝ์—์„œ ๋ฒ—์–ด๋‚˜๊ณ 


๋˜ ๋„ฃ์œผ๋ ค๊ณ  ํ•˜๋ฉด ์˜๋„๋Œ€๋กœ ์ž˜ ๋ง‰ํž ๊ฒƒ์ด๋‹ค.




์ธ๋ฑ์‹ฑ ๊ณ ๋ ค

partial index๋Š” ์‹ค์ œ๋กœ ๋ถ€๋ถ„์ ์œผ๋กœ๋งŒ ์ธ๋ฑ์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋Š”๋งŒํผ, ๊ทธ ๋ถ€๋ถ„ ์กฐ๊ฑด์— ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์ธ๋ฑ์Šค๋ฅผ ํƒ€์ง€ ์•Š๋Š”๋‹ค.

ํ•œ๋ฒˆ ์‹คํ—˜ํ•ด๋ณด์ž.

์ผ๋‹จ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ช‡๊ฐœ ์ด์ƒ์€ ๋˜์–ด์•ผ ์ธ๋ฑ์Šค๋กœ ํ”Œ๋žœ์„ ์žก๊ธฐ ๋–„๋ฌธ์—, ์“ฐ๋ ˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ ๋‹นํžˆ ๋„ฃ์–ด์ค€๋‹ค.

Insert Into "user"(email, password, name, leftAt)
select t.n::text, 'auto generated', 'auto', null from (
  select generate_series(1, 10000) as n
) as t;

๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ƒฅ email๋กœ๋งŒ ์กฐ๊ฑด์„ ๊ฑธ๊ฑฐ๋‚˜

leftAt IS NOT NULL์„ ๊ฑธ๋ฉด ์ธ๋ฑ์Šค๋ฅผ ์ ˆ๋Œ€ ํƒ€์ง€ ์•Š๋Š”๋‹ค. ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


์ธ๋ฑ์Šค๋ฅผ ํƒ€๋ ค๋ฉด ๋ฌด์กฐ๊ฑด Partial Index์—์„œ ์ •์˜๋œ ์กฐ๊ฑด์ด ๋ช…์‹œ๋˜์–ด์•ผ๋งŒ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ Partial Unique Index๋กœ ์œ ์ผ์„ฑ์„ ๊ตฌํ˜„ํ•œ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„, ์‹ค์งˆ์ ์œผ๋กœ ์ธ๋ฑ์‹ฑ์— ์‚ฌ์šฉํ•  ์‹ค์ œ ์ „์ฒด ๋ฒ”์œ„์˜ ์ธ๋ฑ์Šค๋Š” ๋”ฐ๋กœ ๋งˆ๋ จํ•ด์ฃผ๋Š” ํŽธ์ด ์ข‹๋‹ค.



์ฐธ์กฐ
https://medium.com/little-programming-joys/unique-partial-indexes-with-postgresql-86e137905c12