수치 데이터 타입

수치 데이터 타입

PostgreSQL의 수치 타입은 2·4·8바이트 정수, 4·8바이트 부동소수점, 그리고 정밀도를 직접 고를 수 있는 소수 타입으로 나뉘어요. 정확한 계산이 필요한 금액 같은 값과, 대략적인 계산으로 충분한 값이 무엇인지에 따라 타입 선택이 달라지죠. 이번에는 각 타입의 특징과 선택 기준을 살펴볼게요.

출처: PostgreSQL 공식 문서 — Numeric Types

본문

정수 타입

정수 타입은 2바이트 smallint, 4바이트 integer, 8바이트 bigint로 나뉘어요. 범위가 각각 -32768부터 +32767, -2147483648부터 +2147483647, -9223372036854775808부터 +9223372036854775807까지예요. 범위를 넘는 정수를 저장하려고 하면 오류가 나요. 그래서 값의 범위가 어느 정도일지 미리 생각하고 타입을 고르는 게 좋아요.

난이도: 정확한 소수 vs 부동소수점

numeric(혹은 동등한 decimal) 타입은 사용자가 지정한 정밀도로 값을 정확하게 저장해요. 소수점 앞은 최대 131072자리, 소수점 뒤는 최대 16383자리까지 저장할 수 있어요. 반대로 realdouble precision은 가변 정밀도 방식이라 값이 정확하지 않아요. real은 약 6자리, double precision은 약 15자리 십진 정밀도를 갖죠.

정확한 계산이 필요한 금액 계산 같은 경우엔 numeric이 맞고, 계산 속도가 더 중요하고 아주 정밀한 값이 필요 없을 땐 부동소수점 타입을 써요.

반올림 방식도 달라요. numeric은 값을 반올림할 때 0에서 멀어지는 방향으로, 대부분의 머신에서 realdouble precision은 가장 가까운 짝수로 반올림해요. 예를 들어 볼게요.

SELECT x,
  round(x::numeric) AS num_round,
  round(x::double precision) AS dbl_round
FROM generate_series(-3.5, 3.5, 1) as x;
  x   | num_round | dbl_round
------+-----------+-----------
 -3.5 |        -4 |        -4
 -2.5 |        -3 |        -2
 -1.5 |        -2 |        -2
 -0.5 |        -1 |        -0
  0.5 |         1 |         0
  1.5 |         2 |         2
  2.5 |         3 |         2
  3.5 |         4 |         4
(8 rows)

같은 2.5numeric은 3으로, double precision은 2로 반올림되는 걸 볼 수 있어요. 대부분 그 차이가 미미하지만, 딱 절반에 걸치는 값에서는 반올림 결과가 달라질 수 있으니 유의해야 해요.

NaN 처리

대부분의 "not-a-number" 개념(IEEE 754)에서는 NaN이 어떤 값과도 같지 않다고 봐요. 하지만 PostgreSQL은 numeric 값이 트리 기반 인덱스에 정렬되어 쓰일 수 있도록, NaN 값을 서로 같다고 취급하고 모든 NaN이 아닌 값보다 크다고 다뤄요. 부동소수점도 마찬가지 방식으로 처리해요.

부동소수점 표기

realdouble precision 외에도 SQL 표준 표기인 floatfloat(p)를 지원해요. 여기서 p는 이진 자릿수로 나타낸 최소 허용 정밀도예요. float(1)부터 float(24)real 타입을, float(25)부터 float(53)double precision을 선택해요.

직렬(serial) 타입

smallserial, serial, bigserial은 자동 증가 정수 타입이에요. 엄밀히 말하면 진짜 타입이라기보다, 시퀀스를 만들어 기본값으로 연결해 주는 편의 문법이에요.

CREATE TABLE tablename (
    colname SERIAL
);

위 선언은 아래와 동일한 의미로 확장돼요.

CREATE SEQUENCE tablename_colname_seq AS integer;
CREATE TABLE tablename (
    colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
);
ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;

NOT NULL 제약이 적용되어 null 값이 들어갈 수 없도록 해주고, 기본값은 시퀀스 생성기에서 할당되죠. 정수 컬럼에 일련번호를 자동으로 채우고 싶을 때 아주 편리해요.

더 알아보기