숫자
숫자를 저장하는 타입은 크게 값의 정확도에 따라 참값(Exact value data type)과 근사값 타입으로 나눌 수 있다.
- 참 값은 소수점 이하 값의 유무에 관계없이 정확히 그 값을 그대로 유지하는 것을 의미한다. 참값을 관리하는 데이터
타입으로는 INTEGER를 포함해 INT로 끝나는 타입과 DECIMAL이 있다. - 근사값은 흔히 부동 소수점이라고 불리는 값을 의미하며, 처음 컬럼에 저장한 값과 조회된 값이 정확하게 일치하지 않고 최대한 비슷한 값을 관리하는 것을 의미한다. 근사값을 관리하는 타입으로는 FLOAT과 DOUBLE이 있다.
또한 값이 저장되는 포맷에 따라 십진 표기법(DECIMAL)과 이진 표기법으로 나눠 볼 수 있다.
- 이진 표기법이란 정수나 실수 타입을 의미한다. 이진 표기법은 한 바이트로 한자리 또는 두 자리 숫자만 저장하는 것이 아니라 28까지의 숫자를 저장할 수 있는 특징이 있기 때문에 숫자 값을 적은 메모리나 디스크 공간에 저장할 수 있다. MySQL의 INTEGER나 BIGINT 등 대부분의 숫자 타입은 모두 이진 표기법을 사용한다.
- 십진 표기법(DECIMAL)은 숫자 값의 각 자리 값을 표현하기 위해 4비트나 한 바이트를 사용해서 표기하는 방법이다. 이는 십진수가 아니라 디스크나 메모리에 십진 표기법으로 저장된다는 것을 의미한다. MySQL의 십진 표기법을 사용하는 타입은 DECIMAL뿐이며, DECIMAL 타입은 금액(돈)처럼 정확하게 소수점까지 관리돼야 하는 값을 저장할 때 사용한다. 또한 DECIMAL 타입은 65자리 숫자까지 표현할 수 있으므로 BIGINT로도 저장할 수 없는 값을 저장할 때 사용된다.
DBMS에서는 근사값은 저장할 때와 조회할 때의 값이 정확히 일치하지 않고, 유효 자리수를 넘어서는 소수점 이하의 값은 계속 바뀔 수 있으므로 복제에서도 마스터와 슬레이브에서 차이가 발생할 수도 있다. MySQL에서 FLOAT이나 DOUBLE과 같은 부동 소수점 타입은 잘 사용하지 않는다. 또한 십진 표기법을 사용하는 DECIMAL 타입은 이진 표기법을 사용하는 타입보다 저장 공간을 2배 이상을 필요로 한다. 매우 큰 숫자 값이나 고정 소수점을 저장해야 하는 것이 아니라면 일반적으로 INTEGER나 BIGINT 타입을 자주 사용하는 편이다.
1. 정수
DECIMAL 타입을 제외하고 정수를 저장하는 데 사용할 수 있는 데이터 타입으로는 5가지가 있다. 저장 가능한 숫자 값의 범위만 다를 뿐 다른 차이는 거의 없다. 정수 타입의 값을 위한 타입은 직관적이다. 입력이 가능한 수의 범위 내에서 최대한 저장 공간을 적게 사용하는 타입을 선택하면 된다.
정수 타입은 UNSIGNED라는 컬럼 옵션을 사용할 수 있다. 정수 컬럼을 생성할 때 UNSIGNED 옵션을 명시하지 않으면 기본적으로 음수와 양수를 동시에 저장할 수 있는 숫자 타입(SIGNED)이 된다. 하지만 UNSINGED 옵션을 정의한 정수 컬럼은 0보다 큰 양의 정수만 저장할 수 있게 되면서 저장할 수 있는 최댓값은 SINGED 타입보다 2배가 더 커진다. AUTO_INCREMENT 컬럼과 같이 음수가 될 수 없는 값을 저장하는 컬럼에 UNSIGNED 옵션을 명시하면 작은 데이터 공간으로 더 큰 값을 저장할 수 있다.
정수 타입에서 UNSIGNED 옵션은 조인할 때 인덱스의 사용 여부에까지 영향을 미치지는 않는다. 즉 UNSIGNED 정수 컬럼과 SIGNED 정수 컬럼을 조인할 때 인덱스를 이용하지 못한다거나 하는 문제는 발생하지 않는다. 하지만 서로 저장되는 값의 범위가 다르므로 외래 키로 사용하는 컬럼이나 조인의 조건이 되는 컬럼은 SIGNED나 UNSIGNED 옵션을 일치시켜 주는 것이 좋다.
2. 부동 소수점
MySQL에서는 부동 소수점을 저장하기 위해 FLOAT과 DOUBLE 타입을 사용할 수 있다.
부동 소수점이라는 이름에서 부동(Floating point)은 소수점의 위치가 고정적이지 않다는 의미인데, 숫자 값의 길이에 따라 유효 범위의 소수점 자리수가 바뀐다. 그래서 부동 소수점을 사용하면 정확한 유효 소수점 값을 식별하기 어렵고 그 값을 따져서 크다 작다 비교를 하기가 쉽지 않은 편이다. 부동 소수점은 근사값을 저장하는 방식이라서 동등 비교(Equal)는 사용할 수 없다.
FLOAT은 일반적으로 정밀도를 명시하지 않으면 4바이트를 사용해 유효 자리수를 8개까지 유지하며, 정밀도가 명시된 경우에는 최대 8바이트까지 저장 공간을 사용할 수 있다. DOUBLE의 경우 8바이트의 저장 공간을 필요로 하며 최대 유효 자리수를 16개까지 유지할 수 있다.
mysql> create table tb_float(fd1 float);
mysql> insert into tb_float values(0.1);
mysql> select * from tb_float where fd1=0.1;
Empty set (0.00 sec)
복제에 참여하는 MySQL 서버에서 부동 소수점을 사용할 때는 주의해야 한다. 부동 소수점 타입의 데이터는 MySQL의 텍스트 기반(바이너리 로그 파일의 쿼리가 텍스트 기반이므로) 복제에서는 마스터와 슬레이브 간의 데이터가 달라질 수 있다. 물론 유효 정수부나 소수부는 달라지지 않겠지만 위에서도 언급했듯이 유효 정수부나 소수부를 눈으로 판별하기는 쉽지 않다.
만약 부동 소수점 값을 저장해야 한다면 유효 소수점의 자리수만큼 10을 곱해서 정수로 만들어 그 값을 정수 타입의 컬럼에 저장하는 방법도 생각해볼 수 있다. 예를 들어 소수점 4자리까지 유효한 GPS 정보를 저장한다고 했을 때 소수점으로 된 좌표 값에 10000을 곱해서 저장하고 조회할 때는 10000으로 나눈 결과를 사용하면 된다.
create table tb_location(
latitude int unsigned,
longitude int unsigned);
insert into tb_location(latitude, longitude) values (37.1422*10000, 131.5208*10000);
select latitude/10000 as latitude, longitude/10000 as longitude
from tb_location
where latitude = 37.1422*10000 and longitude=131.5208*10000;
+----------+-----------+
| latitude | longitude |
+----------+-----------+
| 37.1422 | 131.5208 |
+----------+-----------+
1 row in set (0.00 sec)
3. DECIMAL
부동 소수점에서 유효 범위 이외의 값은 가변적으로 정확한 값을 보장할 수 없다. 즉, 금액이나 대출이자 등과 같이 고정된 소수점까지만 정확해야 할 때는 FLOAT나 DOUBLE 타입을 사용해서는 안된다. 그래서 소수점의 위치가 가변적이지 않은 고정 소수점 타입을 위해 DECIMAL 타입을 제공한다. 비슷한 성격의 타입으로 NUMEIRC 타입도 있다. MySQL에서는 NUMERIC과 DECIMAL은 내부적으로 같은 방식으로 처리되므로 동의어 정도로 이해하면 된다.
MySQL에서 소수점 이하의 값까지 정확하게 관리하려면 DECIAML이나 NUMERIC 타입을 이용해야 한다. DECIMAL 타입은 숫자 하나를 저장하는데 1/2바이트가 필요하므로 한자리나 두 자리 수를 저장하는데 1바이트가 필요하고 세 자리나 네 자리 숫자를 저장하는 데는 2바이트가 필요하다. 즉 DECIMAL로 저장하는 (숫자의 자리 수)/2 의 결과값을 올림 처리한 만큼의 바이트 수가 필요하다. 그리고 DECIMAL 타입과 BIGINT 타입의 값을 곱하는 연산을 간단히 테스트해보면 아주 미세한 차이지만 DECIMAL보다는 BIGINT 타입이 더 빠르다는 사실을 알 수 있다. 결론적으로 소수가 아닌 정수값을 관리하기 위해 DECIMAL이나 NUMERIC 타입을 사용하는 것은 성능상으로나 공간 사용면에서 좋지 않다. 단순히 정수를 관리하고자 한다면 INTEGER나 BIGINT를 사용하는 것이 좋다.
4. 정수 타입의 컬럼을 생성할 때의 주의사항
부동 소수점이나 DECIMAL 타입을 이용해 컬럼을 정의할 때는 타입의 이름 뒤에 괄호로 정밀도를 표시하는 것이 일반적이다. 예를 들어 "DECIMAL(20,5)"을 정의하면 정수부를 15자리까지 소수부를 5자리까지 저장할 수 있다. "DECIMAL(20)"을 정의하면 정수부만 20자리까지 저장할 수 있는 타입의 컬럼이 생성된다.
FLOAT이나 DOUBLE 타입은 저장공간의 크기가 고정형이므로 정밀도를 조절한다고 해서 저장공간의 크기가 바뀌는 것은 아니다. 하지만 DECIMAL 타입은 저장 공간의 크기가 가변적인 데이터 타입이어서 정밀도는 저장 가능한 자리 수를 결정함과 동시에 저장 공간의 크기까지 제한한다.
그런데 부동 소수점(FLOAT, DOUBLE)이나 고정 소수점(DECIMAL)이 아닌 정수 타입을 생성할 때도 똑같이 BIGINT(10)과 같이 괄호로 값의 크기를 명시할 수 있는 문법을 지원한다. 정수 컬럼에서 BIGINT(10)과 같이 타입을 정의하면 정수 값의 길이를 10자리로 제한할 수 있는 것이 아니다. 모든 정수 타입(BIGINT, INTEGER, SMALLINT, TINYINT등)은 이미 고정형 데이터 타입이며, 정수 타입 뒤에 명시되는 괄호는 화면에 표시할 자리 수를 의미할 뿐 저장 가능한 값을 제한하는 용도가 아니다. 여기서 자리수라는 것도 ZEROFILL을 얼마나 할지를 의미하는 자리수다.
-- zerofill : 지정한 자릿수에서 숫자값을 제외한 나머지를 숫자값 앞에 0을 패딩해서 표시한다.
mysql> create table tb_bigint(col1 bigint(10) zerofill);
Query OK, 0 rows affected, 2 warnings (0.03 sec)
mysql> insert into tb_bigint values (123), (12345), (123456789), (1234567890), (12345678901);
Query OK, 5 rows affected (0.02 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from tb_bigint;
+-------------+
| col1 |
+-------------+
| 0000000123 | -- BIGINT 뒤에 명시된 10자리중 숫자값을 제외하고 0이 왼쪽에 패딩됀다.
| 0000012345 |
| 0123456789 |
| 1234567890 |
| 12345678901 |
+-------------+
5 rows in set (0.00 sec)
정수 타입 뒤의 길이 지정은 ZEROFILL 옵션이 없으면 아무런 의미가 없다. ZEROFILL 옵션이 사용되면 자동으로 그 컬럼의 타입은 양의 숫자만 저장할 수 있는 UNSIGNED 타입이 되어 버리기 때문에 주의해야 한다.
'MySQL > MySQL' 카테고리의 다른 글
CONCAT vs || vs GROUP_CONCAT (0) | 2024.12.24 |
---|---|
AUTO_INCREMENT (0) | 2024.12.20 |
문자열 이스케이프(ESCAPE) (0) | 2024.12.20 |
Collation (0) | 2024.12.20 |
문자열(CHAR와 VARCHAR) (0) | 2024.12.13 |