[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์ ๋ํด ํฉ์งํฉ์ ๋ง๋ ๋ค.

์ด๊ฑด ๊ฒน์น๋๊ฒ ์์ผ๋ฉด ์คํจํ๋ค.
-|- ์ฐ์ฐ์๋ 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