15.4 ENUM과 SET

  • ENUM과 SET은 모두 문자열 값을 MySQL 내부적으로 숫자 값으로 맵핑해서 관리하는 타입이다.
  • 일반적으로 데이터베이스를 사용하다 보면 타입이나 상태 등과 같이 수많은 코드 형태의 칼럼을 사용하게 되는데, 실제 데이터베이스에는 이미 인코딩된 알파벳이나 숫자 값만 저장되므로 그 의미를 바로 파악하기가 쉽지 않다는 단점이 있다.

15.4.1 ENUM

  • ENUM 타입은 반드시 하나의 값만 저장할 수 있는데, 이는 다른 일반적인 칼럼과 같은 특성이므로 쉽게 이해할 수 있다.
  • ENUM 타입의 가장 큰 용도는 코드화된 값을 관리하는 것이다.

  • Insert나 update그리고 select등의 쿼리에서 char나 varchar 타입과 같이 문자열처럼 비교하거나 저장할 수 있다. 하지만 MySQL 서버가 실제로 값을 디스크나 메모리에 저장할 때는 사용자로부터 요청된 문자열이 아니라 그 값에 맵핑된 정수 값을 사용한다.
  • ENUM 타입에 사용할 수 있는 최대 문자열의 개수는 65,535개이며, 문자열의 종류가 255개 미만이면 ENUM 타입은 저장 공간으로 1바이트를 사용하고, 그 이상인 경우에는 2바이트까지 사용한다.
  • ENUM 타입을 사용할 때 일반적으로 특정 문자열 값이 어떤 정수값으로 맵핑됐는지는 알 필요가 없다. 하지만 필요하다면 SELECT 쿼리에서 같이 1을 곱한다거나 0을 더하는 연산을 적용하는 방법으로 ENUM 타입의 실제 값을 확인할 수 있다.
  • ENUM 타입에서 맵핑되는 정수 값은 일반적으로 테이블 정의에 나열된 문자열 순서대로 1부터 할당되며, 빈 문자열(““)은 항상 0으로 맵핑된다.
  • ENUM 타입은 저장해야 하는 문자열 값이 길면 길수록 저장 공간을 더 많이 절약할 수 있다.

ENUM 타입의 단점

  • 칼럼에 저장되는 문자열 값이 테이블의 구조(메타 정보)가 되면서 기존의 ENUM 타입에 새로운 값을 추가해야 한다면 테이블의 구조를 변경해야한다.
  • 테이블의 레코드가 많지 않다면 테이블 구조를 변경하는 것은 큰 문제가 아니지만 테이블의 레코드 수가 많다면 서비스를 멈추고 테이블의 구조를 변경해야 할지도 모른다. ENUM 타입에 새로운 문자열 값이 추가만 될 때는 MySQL에서 새로운 테이블을 만들어 FRM 파일만 바꿔주는 비정상적인 방법도 있지만 권장되지 않는다.

ENUM 타입은 우리가 일반적으로 사용하는 상태나 카테고리와 같이 코드화된 칼럼을 MySQL이 자체적으로 제공하는 기능이다.

그래서 ENUM 타입의 칼럼 값으로 정렬을 수행하면 맵핑되기 전의 문자열 값 기준으로 정렬되는 것이 아니라 맵핑된 코드 값으로 정렬이 수행된다. ENUM 타입은 마치 CHAR나 VARCHAR와 같은 문자열 타입처럼 보이지만 사실은 정수 타입의 칼럼이다.

가장 좋은 방법은 ENUM 타입의 칼럼에 대해서는 정렬을 수행하지 않는 것이 가장 좋지만 만약 꼭 ENUM 타입의 인코딩된 값이 아니라 문자열 기준으로 정렬해야 한다면 테이블을 생성할 때 필요한 정렬기준으로 ENUM 타입의 문자열 값을 나열하면 된다.

만약 이미 만들어진 테이블의 ENUM 타입의 문자열 값으로 강제 정렬을 해야 한다면 CAST() 함수로 변환해서 정렬할 수밖에 없다.

15.4.2 SET

  • 문자열 값을 정수 값으로 맵핑해서 저장하는 방식은 똑같다.

  • 문자열 값의 멤버 수가 8개 이하이면 1바이트의 저장 공간을 사용하며, 9개에서 16개 이하이면 2바이트를 사용하고 똑같은 방식으로 최대 8바이트까지 저장 공간을 사용한다.

  • SET 타입의 칼럼에서 “golf”라는 문자열 멤버를 가진 레코드를 검색해야 할 때는 Select 쿼리에서와 같이 FIND_IN_SET() 함수나 LIKE 검색을 이용할 수 있다.
  • SET 타입의 칼럼에 대해 같은지 “=” 비교를 수행하려면 칼럼에 저장된 순서대로 문자열을 나열해야만 검색할 수 있다. 또한 SET 타입의 칼럼에 인덱스가 있더라도 동등 비교 조건을 제외하고 FIND_IN_SET() 함수나 LIKE를 사용하는 쿼리는 인덱스를 사용할 수 없다.

  • 동시에 여러 개의 값을 갖는 SET 타입의 칼럼에 대해 하나의 특정 값을 포함하고 있는지는 FIND_IN_SET() 함수를 사용하면 된다.
SELECT * FROM tb_set WHERE FIND_IN_SET('tennis', fd_set) >= 1;
  • 위 예제와 같이 FIND_IN_SET() 함수의 사용은 fd_set 칼럼에 인덱스가 있어도 효율적으로 해당 인덱스를 이용할 수 없다. 만약 이러한 형태의 검색이 빈번이 사용된다면 SET 타입의 칼럼을 정규화해서 별도로 인덱스를 가진 자식 테이블을 생성하는 것이 좋다.

SET과 ENUM의 차이

  • SET은 하나의 칼럼에 1개 이상의 값을 저장할 수 있다는 점이다.
  • SET 타입의 칼럼은 여러 개의 값을 저장할 수는 있지만 실제 여러 개의 값을 저장하는 공간을 가지는 것이 아니다. 그래서 각 문자열 값에 맵핑되는 정수값은 1씩 증가되는 정수 값이 아니라 2n의 값을 갖게 된다.