[PostgreSQL] Range ํƒ€์ž…

Range๋Š” PostgreSQL์—์„œ ๊ฝค ์œ ์šฉํ•œ ๋ณด์กฐ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋‹ค.

๋ง ๊ทธ๋Œ€๋กœ (A, B) ํ•œ ์Œ์˜ ๋ฒ”์œ„ ๊ฐ’์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์ด๋ฅผ ํ†ตํ•ด ๊ฝค ํŽธ๋ฆฌํ•˜๊ฒŒ ์‹œ๊ฐ„ ๋ฒ”์œ„๋‚˜ ๊ธฐํƒ€ ์ˆซ์ž ๋ฒ”์œ„ ๊ฐ’์— ๋Œ€ํ•œ ๋กœ์ง ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.




Range์˜ ์ข…๋ฅ˜

๋ฒ”์œ„ ํƒ€์ž…์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆซ์ž์™€ ๋‚ ์งœ ํƒ€์ž…๋“ค์— ๋Œ€ํ•ด์„œ๋งŒ ์ง€์›๋œ๋‹ค.
์ด 6๊ฐ€์ง€๋‹ค.

-- ์ˆซ์ž ๋ฒ”์œ„
select int4range(100, 210);
select int8range(100, 210);
select numrange(100, 210);

-- ๋‚ ์งœ ๋ฒ”์œ„
select daterange(date '2025-11-01', date '2025-11-10');
select tsrange('2026-01-01 23:02:50.635806+09', '2026-01-04 23:02:50.635806+09');
select tstzrange('2026-01-01 23:02:50.635806+09', '2026-01-04 23:02:50.635806+09');

์ˆซ์ž Range ํƒ€์ž…๋“ค์€ ๊ฐ๊ฐ ์ˆœ์„œ๋Œ€๋กœ int4, int8, numeric ํƒ€์ž…๋“ค์— ๋Œ€์‘๋œ๋‹ค. ๊ทธ๊ฑธ 2๊ฐœ์”ฉ ๋ถ™์˜€์„ ๋ฟ์ด๋‹ค.

๋‚ ์งœ ํƒ€์ž…๋“ค์€ ์ˆœ์„œ๋Œ€๋กœ date, timestamp, timestamptz ํƒ€์ž…์— ๋Œ€์‘๋œ๋‹ค.




๊ณตํ†ต ํŠน์„ฑ & ์ œ์•ฝ

Range ํƒ€์ž…๋“ค์€ ๋ฒ”์œ„ ๊ฐ’์œผ๋กœ์„œ ๊ฐ–์ถฐ์•ผ ํ•˜๋Š” ์ตœ์†Œํ•œ์˜ ๋ณด์žฅ์„ ์ œ๊ณตํ•œ๋‹ค.
๋ฌด์กฐ๊ฑด ์™ผ์ชฝ ๊ฐ’์ด ์˜ค๋ฅธ์ชฝ๋ณด๋‹ค ์ž‘์•„์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ ์ œ์•ฝ์„ ์œ„๋ฐ˜ํ•˜๋ฉด ์ฆ‰์‹œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

Range๋Š” (์‹œ์ž‘, ๋) ๊ฐ’์œผ๋กœ ๊ตฌ์„ฑ๋˜๋Š”๋ฐ, ์‹œ์ž‘ ๊ฐ’์€ Range์— ํฌํ•จ๋˜์ง€๋งŒ, ๋ ๊ฐ’์€ Range์— ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.
๊ทธ๋ž˜์„œ (10, 100)์˜ ์ง„์งœ ํ‘œํ˜„ ๊ฐ’์€ 10~99๋‹ค.

๋งŒ์•ฝ ์–‘์ชฝ ๊ฐ’์ด ๊ฐ™๋‹ค๋ฉด, empty ๊ฐ’์œผ๋กœ์„œ ํŠน๋ณ„ํ•˜๊ฒŒ ํ‘œํ˜„๋œ๋‹ค.

์ด๊ฑด ์‚ฌ์‹ค์ƒ null์— ๋Œ€์‘๋˜๋Š” ์ƒํƒœ๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.
์ด ์ƒํƒœ์˜ Range ๊ฐ’์— ๋Œ€ํ•ด ์—ฐ์‚ฐ์„ ํ•˜๋ฉด ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฉ์ถœํ•  ํ™•๋ฅ ์ด ๋†’๋‹ค.




Range ์—ฐ์‚ฐ

Range ํƒ€์ž…์€ ๋ฒ”์œ„ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ธฐ๋ณธ์ ์ธ ์—ฐ์‚ฐ์ž๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.
๊ทธ ๊ธฐ๋ณธ์ ์€ ๋ชฉ๋ก์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

@> ์—ฐ์‚ฐ์ž๋Š” Range์— ๋‹จ์ผ ์Šค์นผ๋ผ ๊ฐ’์ด ํฌํ•จ๋˜๋Š”์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค. ํฌํ•จ๋œ๋‹ค๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

&& ์—ฐ์‚ฐ์ž๋Š” 2๊ฐœ์˜ Range๊ฐ€ ์„œ๋กœ ๊ฒน์น˜๋Š”์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค. ๊ฒน์นœ๋‹ค๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


+์€ 2๊ฐœ Range์— ๋Œ€ํ•ด ํ•ฉ์ง‘ํ•ฉ์„ ๋งŒ๋“ ๋‹ค.

์ด๊ฑด ๊ฒน์น˜๋Š”๊ฒŒ ์—†์œผ๋ฉด ์‹คํŒจํ•œ๋‹ค.

-|- ์—ฐ์‚ฐ์ž๋Š” 2๊ฐœ์˜ Range๊ฐ€ ์„œ๋กœ ์ธ์ ‘ํ•ด์žˆ๋Š”์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค.
์•„๋ž˜์˜ ๊ฒฝ์šฐ์—๋Š” ํ•œ์ชฝ์˜ ์‹œ์ž‘๊ฐ’์ด 10์ด๊ณ , ๋‚˜๋จธ์ง€ ํ•œ์ชฝ์˜ ๋๊ฐ’๋„ 10์ด๋ฏ€๋กœ ์ธ์ ‘ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ true๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

isempty ํ•จ์ˆ˜๋Š” Range ๊ฐ’์ด ๋น„์–ด์žˆ๋Š”์ง€(์–‘์ชฝ์ด ๊ฐ™์€์ง€)๋ฅผ ํ™•์ธํ•œ๋‹ค.

upper์™€ lower๋Š” ๊ฐ๊ฐ ์˜ค๋ฅธ์ชฝ ๊ฐ’๊ณผ ์™ผ์ชฝ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋‹ค.

์—ฌ๊ธฐ์„œ ์œ ์˜ํ•  ์ ์€, range๊ฐ€ empty ์ƒํƒœ์ผ ๊ฒฝ์šฐ upper/lower๊ฐ€ null์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.


, <, >=, <= ๊ฐ™์€ ๋น„๊ต ์—ฐ์‚ฐ๋„ ์ œ๊ณตํ•œ๋‹ค. ์˜๋ฏธ๋ก ์€ ํŠน๋ณ„ํ• ๊ฑด ์—†๋‹ค.

๊ธฐ๋ณธ์ ์ธ ์—ฐ์‚ฐ์€ ์ด ์ •๋„๊ณ , ์ „์ฒด ๋ชฉ๋ก์€ ์•„๋ž˜ ๋ฌธ์„œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
https://www.postgresql.org/docs/9.3/functions-range.html




Gist ์ธ๋ฑ์Šค

Range ๊ฐ’์— ๋Œ€ํ•ด์„œ ๋™๋“ฑ ๋น„๊ต๋งŒ ํ•œ๋‹ค๋ฉด ๊ธฐ๋ณธ B-Tree ์ธ๋ฑ์Šค๋กœ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ, ์‚ฌ์‹ค ๊ทธ๋Ÿด๊ฑฐ๋ฉด Range๋ฅผ ์“ธ ์ด์œ ๋„ ์—†์—ˆ์„ ๊ฒƒ์ด๋‹ค. ๋ฒ”์œ„ ๊ฐ’์— ๋™๋“ฑ ๋น„๊ต๋ฅผ ํ•  ์ผ์ด ์–ผ๋งˆ๋‚˜ ๋˜๊ฒ ๋‚˜?

์•„๋ฌดํŠผ Range ํƒ€์ž…์— ๋Œ€ํ•œ Range ์—ฐ์‚ฐ์€ ๋‹จ์ˆœํ•œ ์Šค์นผ๋ผ ๋น„๊ต ์—ฐ์‚ฐ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—, B-Tree ์ธ๋ฑ์Šค๋กœ๋Š” ์ธ๋ฑ์‹ฑ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. Gist๋ผ๋Š” ํŠน์ˆ˜ํ•œ ์ธ๋ฑ์Šค ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ํ•œ๋‹ค.

ํ•œ๋ฒˆ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด์ž.

create table test_range
(
    id int8 PRIMARY KEY,
    range_value int8range NOT NULL
);

INSERT INTO test_range(id, range_value)
VALUES
(1, int8range(10, 100)),
(2, int8range(20, 120)),
(3, int8range(10, 60)),
(4, int8range(20, 30)),
(5, int8range(40, 50)),
(6, int8range(50, 70)),
(7, int8range(50, 60)),
(8, int8range(50, 55)),
(9, int8range(10, 40)),
(10, int8range(10, 60));

INSERT INTO test_range(id, range_value)
SELECT t.n, int8range(t.n, t.n + 100)
FROM
(
    SELECT generate_series(11, 100000) as n
) as t;

์ ๋‹นํžˆ ๊ฐ’์„ ์ง‘์–ด๋„ฃ๊ณ 

๊ธฐ๋ณธ ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด๋ดค๋‹ค.

CREATE INDEX idx_normal_range on test_range(range_value);

๊ทธ๋Ÿผ, ๊ธฐ๋ณธ์ ์ธ ๋™๋“ฑ ๋น„๊ต(=)์— ๋Œ€ํ•ด์„œ๋Š” ์ธ๋ฑ์Šค๋ฅผ ํƒˆ ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ @>, && ๊ฐ™์€ ๋ฒ”์œ„ ์—ฐ์‚ฐ์€ ์ธ๋ฑ์Šค๋ฅผ ํƒ€์ง€ ๋ชปํ•œ๋‹ค.

๋ฌด์‹œํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ธ๋ฑ์Šค ํƒ€์ž…๋งŒ Gist๋กœ ์„ค์ •ํ•ด์„œ ๋งŒ๋“ค๋ฉด, ๊ทธ๋•Œ๋ถ€ํ„ฐ๋Š” ์ธ๋ฑ์Šค๋ฅผ ํƒ„๋‹ค.

CREATE INDEX idx_gist_range ON test_range USING GIST (range_value);

ํฌํ•จ ์—ฐ์‚ฐ๋„ ์ธ๋ฑ์Šค๋ฅผ ์ž˜ ํƒ€๊ณ 

๊ฒน์นจ ์—ฐ์‚ฐ์ด๋‚˜ ์ธ์ ‘ ์—ฐ์‚ฐ ๋“ฑ๋„ ์ธ๋ฑ์Šค๋ฅผ ์ž˜ ํƒ„๋‹ค.

๊ทธ๋ ‡๋‹ค.



์ฐธ์กฐ
https://www.postgresql.org/docs/current/rangetypes.html
https://www.postgresql.org/docs/9.3/functions-range.html
https://blog.mahahrishi.com/effortless-scheduling-with-postgresql-daterange-and-tstzrange-7df7c83d561d