마이그레이션

MySQL에서 CUBRID로 갈아탈 때 알아야 할 것

by cubebridge posted Nov 13, 2012

요즘 NHN에서 개발하는 서비스에 MySQL 대신 CUBRID를 적용하는 사례가 늘고 있습니다. 이 글은 NHN의 여러 부서에서 MySQL을 CUBRID로 전환하는 과정에서 경험한 두 데이터베이스 간의 차이점을 바탕으로 정리한 것으로, 해당 내용은 크게 '칼럼 타입,' 'SQL 구문,' '제공 기능'의 세 부분으로 나눌 수 있습니다.

이 글을 MySQL 5.5와 CUBRID 2008 R4.1 버전을 기준으로 작성했습니다.

칼럼 타입 관련 차이

문자 타입의 대소문자 구분

MySQL은 기본적으로 질의를 수행할 때 문자 타입의 값에 대해 대소문자를 구분하지 않으므로, MySQL에서 대소문자를 구분하려면 테이블을 생성하거나 질의문을 생성할 때 별도의 BINARY 키워드를 추가해야 한다. 이에 반해 CUBRID는 기본적으로 질의를 수행할 때 문자 타입의 값에 대해 대소문자 구분을 지원한다.

다음은 MySQL에서 테이블을 생성할 때 대상 칼럼에 BINARY를 지정하는 예다.

1
2
3
4
5
CREATE TABLE tbl (name CHAR(10) BINARY);
INSERT INTO tbl VALUES('Charles'),('blues');
SELECT * FROM tbl WHERE name='CHARLES';
  
Empty set (0.00 sec)

다음은 MySQL에서 SELECT 문을 수행할 때 대상 칼럼에 BINARY를 지정하는 예다.

1
2
3
4
5
6
7
SELECT * FROM tbl WHERE BINARY name='Charles';
  
+---------+
| name |
+---------+
| Charles |
+---------+

MySQL에서와 같이 CUBRID에서 대소문자를 구분하지 않게 하려면 다음 예에서 보는 바와 같이 대상 칼럼에 UPPER() 함수 혹은 LOWER() 함수를 적용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT * FROM tbl ORDER BY name;
  
name
======================
'Charles '
'blues '
  
  
SELECT * FROM tbl ORDER BY UPPER(name);
  
name
======================
'blues '
'Charles '

위의 예에서 ORDER BY 칼럼에 UPPER() 함수가 적용되어 ORDER BY를 최적화(skip order by: ORDER BY 순서가 곧 인덱스의 순서인 경우 정렬 과정 없이 인덱스의 순서대로 fetch함)할 수 없다. 이 경우 ORDER BY를 최적화하려면 대문자 혹은 소문자로만 구성된 별도의 칼럼을 생성하고 이 칼럼에 인덱스를 구성하는 방법을 생각해 볼 수 있다.

다음은 대소문자를 구분하지 않는 정렬 전용 칼럼 name2를 별도로 추가한 예다.

1
2
3
4
5
6
7
8
9
ALTER TABLE tbl ADD COLUMN (name2 CHAR(10));
UPDATE tbl SET name2=UPPER(name);
CREATE INDEX i_tbl_name2 ON tbl(name2);
SELECT * FROM tbl ORDER BY name2;
  
name name2
============================================
'blues ' 'BLUES '
'Charles ' 'CHARLES '

날짜 타입의 타입 자동 변환

MySQL은 타입 변환에 매우 유연하다. 숫자 타입에 문자열 입력을 허용하고, 그 반대(문자열 타입에 숫자 입력)도 허용할 뿐 아니라, 날짜 타입에 숫자 입력도 허용한다.

CUBRID는 2008 R4.0 버전부터 유연한 타입 변환을 지원하기 시작하여 숫자 타입에 문자열 입력이나 문자열 타입에 숫자 입력이 가능하다. 하지만 MySQL과 달리 날짜 타입에 숫자 입력은 허용하지 않는다.

다음은 MySQL에서 dt(날짜 타입) 칼럼에 숫자를 입력한 예다.

1
2
3
4
5
6
7
8
9
10
mysql> CREATE TABLE dt_tbl(dt DATE);
mysql> INSERT INTO dt_tbl VALUES (20120515);
mysql> SELECT * FROM dt_tbl;
  
+------------+
| dt |
+------------+
| 2012-05-15 |
+------------+
1 row in set (0.00 sec)

다음은 CUBRID에서 dt(날짜 타입) 칼럼에 숫자를 입력한 예로 날짜 타입에 숫자를 입력한 경우 결과 값으로 오류가 반환되는 것을 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
csql> CREATE TABLE dt_tbl(dt DATE);
csql> INSERT INTO dt_tbl VALUES (20120515);
  
ERROR: before ' ); '
Cannot coerce 20120515 to type date.
  
csql> INSERT INTO dt_tbl VALUES ('20120515');
csql> SELECT * FROM dt_tbl;
  
dt
============
05/15/2012

날짜 함수 실행 결과로 오류가 발생하는 경우, 기본적으로 MySQL은 NULL을 반환하고 CUBRID는 오류를 반환한다. CUBRID에서 NULL을 반환하게 하려면 시스템 파라미터인 return_null_on_function_errors의 값을 yes로 설정한다.

다음 예는 MySQL에서 날짜 함수에 유효하지 않은 인자를 입력했을 때 NULL이 반환되는 것을 보여준다.

1
2
3
4
5
6
7
8
mysql> SELECT YEAR('12:34:56');
  
+------------------+
| YEAR('12:34:56') |
+------------------+
| NULL |
+------------------+
1 row in set, 1 warning (0.00 sec)

다음 예는 CUBRID에서 시스템 파라미터 return_null_on_function_error 값을 기본 값인 no로 설정한 상태에서 날짜 함수에 유효하지 않은 인자를 입력했을 때 오류가 반환되는 것을 보여준다.

1
2
3
csql> SELECT YEAR('12:34:56');
  
ERROR: Conversion error in date format.

다음 예는 CUBRID에서 시스템 파라미터 return_null_on_function_errors의 값을 yes로 변경한 상태에서 날짜 함수에 유효하지 않은 인자를 입력했을 때 NULL이 반환되는 것을 보여준다.

1
2
3
4
5
csql> SELECT YEAR('12:34:56');
  
year('12:34:56')
======================
NULL

정수 나누기 정수의 결과 값 타입

정수 값끼리 나누기 연산을 수행할 때 MySQL에서는 DECIMAL(m, n)의 실수 타입의 값이 출력되지만, CUBRID에서는 결과 값을 반올림한 정수 타입의 값이 출력된다. CUBRID에서는 피연산자 타입이 동일하면 해당 타입으로 값이 출력되기 때문이다. 이 경우 출력 값의 타입을 실수로 변경하고 싶다면, CAST() 함수를 사용하여 입력 값 타입을 변경하거나, 둘 중 하나의 값을 실수 타입으로 바꾼다.

다음은 MySQL에서 정수 값끼리 나누기 연산을 수행한 예다. 결과 값은 실수 타입으로 출력된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SELECT 4/3;
  
+--------+
| 4/3 |
+--------+
| 1.3333 |
+--------+
  
mysql> SELECT 4/2;
  
+--------+
| 4/2 |
+--------+
| 2.0000 |
+--------+

다음은 CUBRID에서 정수 값끼리 나누기 연산을 수행한 예다. 결과 값은 정수 타입으로 출력된다.

1
2
3
4
5
6
7
8
9
10
11
csql> SELECT 4/3;
  
4/3
=============
1
  
csql> SELECT 4/2;
  
4/2
=============
2

다음은 CUBRID에서 CAST() 함수를 사용하여 정수 값끼리 나누기 연산을 수행한 예다. 결과 값은 실수 타입으로 출력된다.

1
2
3
4
5
csql> SELECT CAST(4 AS DECIMAL(5,4))/CAST(3 AS DECIMAL(5,4));
  
cast(4 as numeric(5,4))/ cast(3 as numeric(5,4))
======================
1.333333333

다음은 CUBRID에서 정수 값을 실수로 나누기 연산을 수행한 예다. 입력 값 중 하나가 실수 타입이므로 결과 값이 실수(DOUBLE) 타입으로 출력된다.

1
2
3
4
5
csql> SELECT 4/3.0;
  
4/3.0
======================
1.333333333

입력 값 타입의 최댓값보다 큰 총합(SUM) 처리

SUM의 결과가 입력 값 타입의 최댓값보다 크면 결과는 어떻게 출력될까?

MySQL에서느 SUM한 결과를 미리 정의한 큰 자릿수의 DECIMAL 타입으로 변환하지만, CUBRID에서는 이를 오버플로우(overflow) 오류로 처리한다. 즉, CUBRID는 입력 칼럼의 타입이 결과 타입을 결정한다. 따라서, CUBRID에서 오버플로우 오류를 방지하려면 SUM의 결과 값을 수용할 수 있는 타입으로 입력 칼럼의 타입을 변환한 후 연산을 수행해야 한다.

이 경우 타입 변환 시에 CAST() 함수의 실행으로 인한 추가 비용이 발생하므로 처음부터 결과 값을 고려하여 칼럼 타입을 결정할 것을 권장한다.

MySQL과 CUBRID 두 데이터베이스에 다음과 같이 테이블을 동일하게 구성한다..

1
2
3
CREATE TABLE t (code SMALLINT);
INSERT INTO t