DB에 실수(float)를 저장할 때 FLOAT 타입을 사용하면 안 된다 굽쇼?

By | 2011년 6월 30일

개발 언어에서는 실수형 데이터를 float 타입으로 선언하곤 합니다. 이 때문에 DB를 구성할 때도 실수형 데이터가 저장될 열을 FLOAT 타입으로 선언하지요.

ALTER TABLE 테이블이름 ADD COLUMN 컬럼이름 FLOAT;

하지만, 실제로 데이터를 저장한 다음 평균을 내거나 합계를 내보면 조금씩 차이가 나는 현상을 발견하게 됩니다.
이유는 바로 FLOAT 타입이 근사 값을 저장하기 때문입니다. (FLOAT, REAL, DOUBLE PRECISION 모두 동일합니다.)
이는 msdn에서도 다음과 같이 명확히 설명하고 있습니다.

부동 소수점 데이터는 근사 값이므로 해당 데이터 형식 범위에 있는 모든 값을 정확하게 표현할 수는 없습니다.
– 출처 : msdn

조금 헷갈리시겠지만, 실수(float)형 데이터를 DB에 저장할 때는 해당 열을 FLOAT 타입이 아닌 NUMERIC이나 DECIMAL 형으로 선언해야 합니다. (Oracle에서는 BINARY_FLOAT에서 문제가 생깁니다. 그냥 FLOAT은 괜찮답니다.)

이런 식으로 개발자가 SQL을 작성할 때, 너무나 일반적으로 사용하고 있지만(Pattern) 자세히 들춰보면 옳지 않은 방법을 SQL AntiPattern(안티패턴)이라고 합니다.

이번에 저희가 출간한 SQL AntiPatterns에는 이런 예들이 무려 25가지나 소개됩니다.

재미있는 안티패턴을 또 하나 소개하자면… 바로 NULL 설정과 관련된 예입니다.

사용자 테이블에서 email 열에 NULL 값이 허용되었을 때, email을 입력하지 않은 사용자를 찾기 위해서는 보통 다음과 같은 SQL을 머릿속에 떠올릴 것입니다.

SELECT * FROM 테이블이름 WHERE email = NULL;

하지만 이 SQL문의 결과는 없습니다(NULL). NULL과는 어떤 값을 비교해도 결과가 False이기 때문이죠. 무슨 말이냐고요? 다음과 같은 예를 들어 보죠. (저자가 책에서 설명한 예를 조금 각색했습니다. ^^)

윤아의 나이가 22세이고, 효연의 나이를 모른다고 할 때, ‘윤아가 효연이보다 나이가 많아?’라는 질문에 대한 답은 ‘모른다’입니다. 반대도 마찬가지고요. 둘의 나이가 같으냐고 물어도 역시 ‘모른다’고 대답할 수밖에 없죠.
서현이의 나이도 모른다고 해보죠. 서현이와 효연이의 나이가 같은지 물어본다면 역시나 ‘모른다’고 말할 수밖에 없습니다. 이것이 바로 NULL = NULL의 결과가 False인 이유입니다.
(얼마 전 소녀시대가 프랑스에서 날렸다기에 적어본 것 뿐입니다. 절대로. 그 이유 뿐이예요.)

그렇다면, email 열에 어떤 값이라도 들어있는, 즉 NULL이 아닌 사용자를 찾기 위해 다음처럼 SQL을 만드는 건 어떨까요?

SELECT * FROM 테이블이름 WHERE NOT (email = NULL);

혹은

SELECT * FROM 테이블이름 WHERE email <> NULL;

역시나 결과는 NULL입니다. 서현이와 효연이의 나이가 같은지 물어봤을 때 ‘모른다’고 대답했듯이, 둘의 나이가 다르냐고 물어봐도 역시나 ‘모른다’고 대답할 밖에요.

자… 여기까지 보셨으면 다른 안티패턴들은 어떤 내용인지 궁금하실텐데요~. (궁금하셔야만 하는 겁니다. ㅎㅎ)
목차가 궁금하신 분은 아래의 ‘more view’를 눌러보세요~ ^^

[#M_더보기|접기|옮긴이의 글
한국어판 지은이의 글
 
1장 개요

    1.1 대상 독자
    1.2 이 책에 있는 내용
    1.3 이 책에 없는 내용
    1.4 일러두기
    1.5 예제 데이터베이스
    1.6 감사의 글
 
2장 무단횡단

    2.1 목표: 다중 값 속성 저장
    2.2 안티패턴: 쉼표로 구분된 목록에 저장
    2.3 안티패턴 인식 방법
    2.4 안티패턴 사용이 합당한 경우
    2.5 해법: 교차 테이블 생성

 3장 순진한 트리

    3.1 목표: 계층구조 저장 및 조회하기
    3.2 안티패턴: 항상 부모에 의존하기
    3.3 안티패턴 인식 방법
    3.4 안티패턴 사용이 합당한 경우
    3.5 해법: 대안 트리 모델 사용
 
4장 아이디가 필요해

    4.1 목표: PK 관례 확립
    4.2 안티패턴: 만능키
    4.3 안티패턴 인식 방법
    4.4 안티패턴 사용이 합당한 경우
    4.5 해법: 상황에 맞추기
 
5장 키가 없는 엔트리

    5.1 목표: 데이터베이스 아키텍처 단순화
    5.2 안티패턴: 제약조건 무시
    5.3 안티패턴 인식 방법
    5.4 안티패턴 사용이 합당한 경우
    5.5 해법: 제약조건 선언하기
 
6장 엔터티-속성-값

    6.1 목표: 가변 속성 지원
    6.2 안티패턴: 범용 속성 테이블 사용
    6.3 안티패턴 인식 방법
    6.4 안티패턴 사용이 합당한 경우
    6.5 해법: 서브타입 모델링
 
7장 다형성 연관

    7.1 목표: 여러 부모 참조
    7.2 안티패턴: 이중 목적의 FK 사용
    7.3 안티패턴 인식 방법
    7.4 안티패턴 사용이 합당한 경우
    7.5 해법: 관계 단순화

 8장 다중 칼럼 속성

    8.1 목표: 다중 값 속성 저장
    8.2 안티패턴: 여러 개의 칼럼 생성
    8.3 안티패턴 인식 방법
    8.4 안티패턴 사용이 합당한 경우
    8.5 해법: 종속 테이블 생성
 
9장 메타데이터 트리블

    9.1 목표: 확장 적응성 지원
    9.2 안티패턴: 테이블 또는 칼럼 복제
    9.3 안티패턴 인식 방법
    9.4 안티패턴 사용이 합당한 경우
    9.5 해법: 파티션과 정규화
 
10장 반올림 오류

    10.1 목표: 정수 대신 소수 사용
    10.2 안티패턴: FLOAT 데이터 타입 사용
    10.3 안티패턴 인식 방법
    10.4 안티패턴 사용이 합당한 경우
    10.5 해법: NUMERIC 데이터 타입 사용
 
11장 31가지 맛

    11.1 목표: 칼럼을 특정 값으로 제한하기
    11.2 안티패턴: 칼럼 정의에 값 지정.
    11.3 안티패턴 인식 방법
    11.4 안티패턴 사용이 합당한 경우
    11.5 해법: 데이터로 값을 지정하기
 
12장 유령 파일

    12.1 목표: 이미지 또는 벌크 미디어 저장
    12.2 안티패턴: 파일을 사용해야 한다고 가정한다
    12.3 안티패턴 인식 방법
    12.4 안티패턴 사용이 합당한 경우
    12.5 해법: 필요한 경우에는 BLOB 데이터 타입을 사용하라
 
13장 인덱스 샷건

    13.1 목표: 성능 최적화
    13.2 안티패턴: 무계획하게 인덱스 사용하기
    13.3 안티패턴을 인식하는 방법
    13.4 안티패턴 사용이 합당한 경우
    13.5 해법: 인덱스를 MENTOR하라

 14장 모르는 것에 대한 두려움

    14.1 목표: 누락된 값을 구분하기
    14.2 안티패턴: NULL을 일반 값처럼 사용
    14.3 안티패턴 인식 방법
    14.4 안티패턴 사용이 합당한 경우
    14.5 해법: 유일한 값으로 NULL을 사용하라
 
15장 애매한 그룹

    15.1 목표: 그룹당 최댓값을 가진 행 얻기
    15.2 안티패턴: 그룹되지 않은 칼럼 참조
    15.3 안티패턴 인식 방법
    15.4 안티패턴 사용이 합당한 경우
    15.5 해법: 칼럼을 모호하게 사용하지 않기

 16장 임의의 선택

    16.1 목표: 샘플 행 가져오기
    16.2 안티패턴: 데이터를 임의로 정렬하기
    16.3 안티패턴 인식 방법
    16.4 안티패턴 사용이 합당한 경우
    16.5 해법: In No Particular Order
 
17장 가난한 자의 검색 엔진

    17.1 목표: 전체 텍스트 검색
    17.2 안티패턴: 패턴 매칭 사용
    17.3 안티패턴 인식 방법
    17.4 안티패턴 사용이 합당한 경우
    17.5 해법: 작업에 맞는 올바른 도구 사용하기

 18장 스파게티 쿼리

    18.1 목표: SQL 쿼리 줄이기
    18.2 안티패턴: 복잡한 문제를 한 번에 풀기
    18.3 안티패턴 인식 방법
    18.4 안티패턴 사용이 합당한 경우
    18.5 해법: 분할해서 정복하기
 
19장 암묵적 칼럼

    19.1 목표: 타이핑 줄이기
    19.2 안티패턴: 지름길만 좋아하면 길을 잃는다
    19.3 안티패턴 인식 방법
    19.4 안티패턴 사용이 합당한 경우
    19.5 해법: 명시적으로 칼럼 이름 지정하기
 
20장 읽을 수 있는 패스워드

    20.1 목표: 패스워드를 복구하거나 재설정하기
    20.2 안티패턴: 패스워드를 평문으로 저장하기
    20.3 안티패턴 인식 방법
    20.4 안티패턴 사용이 합당한 경우
    20.5 해법: 패스워드의 소금 친 해시
 
21장 SQL 인젝션

    21.1 목표: 동적 SQL 쿼리 작성하기
    21.2 안티패턴: 검증되지 않은 입력을 코드로 실행하기
    21.3 안티패턴 인식 방법
    21.4 안티패턴 사용이 합당한 경우
    21.5 해법: 아무도 믿지 마라
 
22장 가상키 편집증

    22.1 목표: 데이터 정돈하기
    22.2 안티패턴: 모든 틈 메우기
    22.3 안티패턴 인식 방법
    22.4 안티패턴 사용이 합당한 경우
    22.5 해법: 극복하라
 
23장 나쁜 것 안 보기

    23.1 목표: 코드를 적게 작성하기
    23.2 안티패턴: 짚 없이 벽돌 만들기
    23.3 안티패턴 인식 방법
    23.4 안티패턴 사용이 합당한 경우
    23.5 해법: 에러에서 우아하게 복구하기
 
24장 외교적 면책특권

    24.1 목표: 관례 따르기
    24.2 안티패턴: SQL을 2등 시민으로 만들기
    24.3 안티패턴 인식 방법
    24.4 안티패턴 사용이 합당한 경우
    24.5 해법: 초당적 품질 문화 확립
 
25장 마법의 콩

    25.1 목표: MVC에서 모델 단순화하기
    25.2 안티패턴: 액티브 레코드인 모델
    25.3 안티패턴을 인식하는 방법
    25.4 안티패턴 사용이 합당한 경우
    25.5 해법: 액티브 레코드를 가지는 모델
 
부록 A 정규화 규칙

    A.1 관계형의 뜻
    A.2 정규화에 대한 미신
    A.3 정규화란?
    A.4 상식
 
부록 B 참고문헌_M#]

남들이 하는 대로 SQL을 사용하기보다, 올바른 방식으로 SQL을 사용하려는 모든 개발자들께서는 지금 온라인 서점으로 달려가시면 되겠습니다. ^^

구입 – 교보문고, 알라딘, 예스24, 인터파크

.

One thought on “DB에 실수(float)를 저장할 때 FLOAT 타입을 사용하면 안 된다 굽쇼?

  1. 레몬에이드

    이야 그런것도 있었군요. 전 DB를 사용하질 않아서 몰랐는데 말이죠 ^^ 우리 웹팀에게 추천해야겠습니다. 좋아라하겠네요~

    Reply

댓글 남기기