:meta-keywords: cubrid data types, cubrid type conversion, cubrid numeric types, cubrid date time, cubrid strings, cubrid character, cubrid enum, cubrid blob/clob, cubrid collection types, cubrid json type :meta-description: All CUBRID data types and conversion rules. *********** 데이터 타입 *********** 수치형 데이터 타입 ================== 정수 또는 실수를 저장하기 위해 다음의 수치형 데이터 타입을 지원한다. +----------------------+-----------+----------------------------+----------------------------+--------------------+ | 타입 | Bytes | 최소값 | 최대값 | 정확/근사치 | +======================+===========+============================+============================+====================+ | **SHORT**, | 2 | -32,768 | 32,767 | 정확한 수치 | | **SMALLINT** | | | | | +----------------------+-----------+----------------------------+----------------------------+--------------------+ | **INTEGER**, | 4 | -2,147,483,648 | +2,147,483,647 | 정확한 수치 | | **INT** | | | | | +----------------------+-----------+----------------------------+----------------------------+--------------------+ | **BIGINT** | 8 | -9,223,372,036,854,775,808 | +9,223,372,036,854,775,807 | 정확한 수치 | +----------------------+-----------+----------------------------+----------------------------+--------------------+ | **NUMERIC**, | 16 | 정밀도 *p*: 1 | 정밀도 *p*: 38 | 정확한 수치 | | **DECIMAL** | | | | | | | | 스케일 *s*: 0 | 스케일 *s*: 38 | | +----------------------+-----------+----------------------------+----------------------------+--------------------+ | **FLOAT**, | 4 | -3.402823466E+38 | +3.402823466E+38 | 근사치 | | **REAL** | | (ANSI/IEEE 754-1985 표준) | (ANSI/IEEE 754-1985 표준) | 부동소수점: 7자리 | +----------------------+-----------+----------------------------+----------------------------+--------------------+ | **DOUBLE**, | 8 | -1.7976931348623157E+308 | +1.7976931348623157E+308 | 근사치 | | **DOUBLE PRECISION** | | (ANSI/IEEE 754-1985 표준) | (ANSI/IEEE 754-1985 표준) | 부동소수점: 15자리 | +----------------------+-----------+----------------------------+----------------------------+--------------------+ 수치형 데이터 타입은 정확한(exact) 타입과 근사치(approximate) 타입으로 구분된다. 정확한 수치형 데이터 타입(**SMALLINT**, **INT**, **BIGINT**, **NUMERIC**)은 정확하고 일관된 값을 가져야 하는 경우에 사용된다. 근사치 수치형 데이터 타입(**FLOAT**, **DOUBLE**)은 리터럴 값이 같아도 시스템에 따라 다르게 해석될 수 있으므로 주의한다. CUBRID는 수치형 데이터 타입에 대해 UNSIGNED 타입을 지원하지 않는다. 위의 표에서 두 가지 이름으로 표기한 타입들은 동일하지만, 테이블 생성 후 :ref:`show-columns-statement` 문으로 타입의 이름을 확인할 때에는 항상 위 단어로 표기된다. 예를 들어, 테이블을 생성할 때에는 **SHORT**, **SMALLINT** 둘 다 쓸 수 있으며, :ref:`show-columns-statement` 문으로 타입의 이름을 확인할 때에는 항상 **SHORT** 로 표기된다. **정밀도와 스케일(Precision and Scaling)** 숫자 데이터 타입의 정밀도(precision)는 그 값이 유지할 수 있는 유효한 자릿수로 정의된다. 이는 정확한 수치형이든 근사치 수치형이든 마찬가지이다. 스케일(scale)은 소수점 이하의 자릿수를 나타내는데, 정확한 수치형에서만 의미가 있다. 정확한 수치형으로 선언된 속성은 항상 고정된 정밀도와 스케일을 갖게 된다. **NUMERIC** (또는 **DECIMAL**) 데이터 타입은 항상 최소한 한 자리의 정밀도를 갖는다. 스케일의 범위는 0과 선언된 정밀도 사이여야 한다. 스케일이 정밀도보다 클 수는 없다. **INTEGER** 나 **SMALLINT**, **BIGINT** 데이터 타입에서는 스케일은 0이고(즉, 소수점 이하가 없음), 정밀도는 시스템에 의해 고정된다. **수치형 리터럴(Numeric Literals)** 수치형 값을 입력하기 위해서는 특별한 기호가 사용될 수 있는데, 플러스(+)는 양수를, 마이너스(-)는 음수를 나타내는 데 사용한다. 과학용 표기법이 사용될 수도 있다. 화폐 값을 표현하기 위하여 시스템에 지정된 통화 기호를 사용할 수도 있다. 수치형 리터럴로 표현 가능한 최대 정밀도는 255이다. **수치형 변환(Numeric Coercions)** 모든 수치형 데이터 타입은 상호 비교 가능하고, 이를 위해 서로 공통된 수치형 데이터 타입으로 자동 변환이 이루어진다. 명시적인 변환은 **CAST** 연산자를 이용해야 한다. 서로 다른 수치형 데이터가 서로 정렬되거나, 수식에서 계산될 때에는 시스템에 의하여 자동으로 변환된다. 예를 들어, **FLOAT** 타입의 속성 값과 **INTEGER** 타입의 속성 값을 더하게 되면, 시스템이 자동적으로 **INTEGER** 속성 값을 가장 근사한 **FLOAT** 값으로 변환한 후 덧셈을 수행한다. 다음은 **FLOAT** 타입 값과 **INTEGER** 타입 값을 더하는 경우 **FLOAT** 타입 값을 출력하는 예이다. .. code-block:: sql CREATE TABLE tbl (a INT, b FLOAT); INSERT INTO tbl VALUES (10, 5.5); SELECT a + b FROM tbl; :: 1.550000e+01 다음은 두 개의 정수 값을 더하는 경우 결과 값도 **INTEGER** 타입이 되기 때문에 오버플로우(overflow) 에러가 발생하는 예이다. .. code-block:: sql SELECT 100000000*1000000; :: ERROR: Data overflow on data type integer. 위와 같은 경우 어느 한 쪽의 타입을 **BIGINT**\ 로 명시하면, **BIGINT**\ 로 결과 값을 결정하고, 정상적인 결과를 출력한다. .. code-block:: sql SELECT CAST(100000000 AS BIGINT)*1000000; :: 100000000000000 .. warning:: CUBRID 2008 R2.0 미만 버전에서는 입력된 상수가 **INTEGER** 범위를 넘어서면 **NUMERIC**\ 으로 처리되었으나, CUBRID 2008 R2.0 이상 버전에서는 **BIGINT**\ 로 처리된다. INT, INTEGER ------------ **INTEGER** 데이터 타입은 정수 표현을 위해 사용하며, 표현할 수 있는 값의 범위는 -2,147,483,648에서 +2,147,483,647이다. 작은 정수를 표현하기 위해 **SMALLINT**\ 를 사용하거나, 큰 정수를 표현하기 위해 **BIGINT**\ 를 사용할 수 있다. * **INTEGER**\ 와 **INT**\ 는 같은 의미로 사용된다. * **INT** 타입에 실수가 입력되면, 소수점 아래 숫자가 반올림되어 정수값이 저장된다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: INTEGER에 8934를 지정하면 8934가 저장됨. INTEGER에 7823467를 지정하면 7823467이 저장됨. INTEGER에 89.8를 지정하면 90이 저장됨(소수점 뒤의 수치는 반올림됨). INTEGER에 3458901122를 지정하면 오류가 발생함(표현 가능 범위를 초과하면 오류 발생). SHORT, SMALLINT --------------- **SMALLINT** 데이터 타입은 작은 정수 표현을 위해 사용되며, 표현할 수 있는 값의 범위는 -32,768에서 +32,767이다. * **SMALLINT**\ 와 **SHORT**\ 는 같은 의미로 사용된다. * **SMALLINT** 타입에 실수가 입력되면, 소수점 아래 숫자가 반올림되어 정수값이 저장된다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: SMALLINT에 8934를 지정하면 8934가 저장됨. SMALLINT에 34.5를 지정하면 35가 저장됨(소수점 이하의 숫자는 반올림됨). SMALLINT에 23467를 지정하면 23467이 저장됨. SMALLINT에 89354를 지정하면 오류가 발생함(표현 가능 범위를 초과하면 오류 발생). BIGINT ------ **BIGINT** 데이터 타입은 큰 정수 표현을 위해 사용되며, 표현할 수 있는 값의 범위는 -9,223,372,036,854,775,808에서 9,223,372,036,854,775,807이다. * **BIGINT** 타입에 실수가 입력되면, 소수점 아래 숫자가 반올림되어 정수값이 저장된다. * 정밀도와 표현할 수 있는 범위를 기준으로는 다음과 같이 정렬할 수 있다. **SMALLINT** ⊂ **INTEGER** ⊂ **BIGINT** ⊂ **NUMERIC** * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: BIGINT에 8934를 지정하면 8934가 저장됨. BIGINT에 89.1을 지정하면 89가 저장됨. BIGINT에 89.8을 지정하면 90이 저장됨(소수점 뒤의 수치는 반올림됨). BIGINT에 3458901122를 지정하면 3458901122가 저장됨. NUMERIC, DECIMAL ---------------- **NUMERIC** 또는 **DECIMAL** 데이터 타입은 고정 소수점 숫자를 표현하기 위해 사용되며, 다음과 같이 전체 자리 수(정밀도)와 소수점 아래 자릿수(스케일)을 옵션으로 지정하여 정의할 수 있다. 정밀도 *p*\ 의 최소값은 1이고 최대값은 38이며, 정밀도 *p*\ 가 생략되면 기본값은 15이므로, 정수부가 15자리를 초과하는 데이터를 입력할 수 없다. 또한, 스케일 *s*\ 가 생략되면 스케일의 기본값은 0이므로 소수점 아래 첫째 자리에서 반올림한 정수를 반환한다. :: NUMERIC [(p[, s])] * 정밀도는 반드시 스케일 이상이어야 한다. * 정밀도는 (데이터의 정수부 자리 수 + 스케일) 이상이 되도록 지정한다. * **NUMERIC**\ 과 **DECIMAL**, 그리고 **DEC**\ 는 같은 의미로 사용된다. * **NUMERIC** 타입끼리 연산한 결과 값의 정밀도와 스케일이 어떻게 달라지는지 확인하려면 :ref:`numeric-data-type-op-and-conversion`\ 을 참고한다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: NUMERIC에 12345.6789를 지정하면 12346이 저장됨(스케일 기본값은 0이므로 소수점 아래 첫째 자리에서 반올림함). NUMERIC(4)에 12345.6789를 지정하면 오류가 발생함(정밀도는 데이터의 정수부 자릿수 이상이어야 함). NUMERIC(3,4)를 선언하면 오류가 발생함(정밀도는 스케일 이상이어야 함). NUMERIC(4,4)에 0.123456789를 지정하면 .1235가 저장됨(소수점 아래 다섯째 자리에서 반올림함). NUMERIC(4,4)에 -0.123456789를 지정하면 -.1235가 저장됨(소수점 아래 다섯째 자리에서 반올림한 후, - 부호를 붙임). FLOAT, REAL ----------- **FLOAT** (또는 **REAL**) 데이터 타입은 부동 소수점 숫자를 표현하기 위해 사용된다. 정규 값(normalized value)으로 표현할 수 있는 값의 범위는 -3.402823466E+38 에서 -1.175494351E-38, 0, 그리고 +1.175494351E-38 에서+3.402823466E+38이며, 이 범위를 벗어나서 0에 가까운 값은 비정규 값(denormalized value)으로 표현한다. 이는 ANSI/IEEE 754-1985 표준을 준수한다. 정밀도 *p*\ 의 최소값은 1이고 최대값은 38이며, 정밀도 *p*\ 가 생략되거나 7 이하로 지정되면 단일 정밀도(single-precision, 7자리의 유효 숫자)로 표현된다. 만약 정밀도 *p*\ 가 7보다 크고 38 이하이면 이중 정밀도(double-precision, 15자리의 유효 숫자)로 표현되며, **DOUBLE** 데이터 타입으로 변환된다. **FLOAT** 데이터 타입은 7자리의 유효 자릿수를 넘는 입력 값에 대해 근사치를 저장하는 타입이므로 유효 자릿수를 넘어서는 정확한 값을 저장하려면 사용하지 않도록 주의한다. :: FLOAT[(p)] * **FLOAT** 타입의 유효 자리 수는 7이다. * **FLOAT** 타입은 근사치 데이터를 저장하므로 데이터 비교 시 주의해야 한다. * **FLOAT**\ 와 **REAL**\ 은 같은 의미로 사용된다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: FLOAT에 16777217을 입력하면 16777216이 저장되고, 1.677722e+07이 출력된다(정밀도가 생략되면, 7개의 유효 숫자로 표현하므로 8번째 숫자를 반올림함). FLOAT(5)에 16777217을 입력하면 16777216이 저장되고, 1.677722e+07이 출력된다(정밀도가 7 이하이면, 7개의 유효 숫자로 표현하므로 8번째 숫자를 반올림함). FLOAT(5)에 16777.217을 입력하면 16777.216이 저장되고, 1.677722e+04가 출력된다(정밀도가 7 이하이면, 7개의 유효 숫자로 표현하므로 8번째 숫자를 반올림함). FLOAT(10)에 16777.217를 지정하면 16777.217이 저장되고, 1.677721700000000e+04가 출력된다(정밀도가 7보다 크고 38 이하이면, DOUBLE 타입으로 변환되어 15개의 유효 숫자로 표현하므로 0을 채움). DOUBLE, DOUBLE PRECISION ------------------------ **DOUBLE** 데이터 타입은 부동 소수점 숫자를 표현하기 위해 사용된다. 정규 값(normalized value)으로 표현할 수 있는 값의 범위는 -1.7976931348623157E+308에서 -2.2250738585072014E-308, 0, 그리고 2.2250738585072014E-308에서 1.7976931348623157E+308이며, 이 범위를 벗어나서 0에 가까운 값은 비정규 값(denormalized value)으로 표현한다. 이는 ANSI/IEEE 754-1985 표준을 준수한다. 정밀도를 지정할 수 없으며, 이 타입이 지정된 데이터는 이중 정밀도(double-precision, 15자리의 유효 숫자)로 표현된다. **DOUBLE** 데이터 타입은 15자리의 유효 자릿수를 넘는 입력 값에 대해 근사치를 저장하는 타입이므로 유효 자릿수를 넘어서는 정확한 값을 지정할 때에는 사용하지 않도록 주의한다. * **DOUBLE**\ 의 유효 자리 수는 15자리이다. * **DOUBLE** 타입은 근사치 데이터를 저장하므로 데이터 비교 시 주의해야 한다. * **DOUBLE**\ 과 **DOUBLE PRECISION**\ 은 같은 의미로 사용된다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: DOUBLE에 1234.56789를 입력하면 1234.56789가 저장되고, 1.234567890000000e+03이 출력된다. DOUBLE에 9007199254740993을 입력하면 9007199254740992가 저장되고, 9.007199254740992e+15가 출력된다. .. note:: MONETARY 타입은 제거될 예정이며(deprecated), 더 이상 사용을 권장하지 않는다. .. _date-time-type: 날짜/시간 데이터 타입 ===================== 날짜/시간 데이터 타입은 날짜, 시간 혹은 이 두 가지를 모두 표현할 때 사용하는 데이터 타입으로 다음과 같은 데이터 타입을 지원한다. +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ | 타입 | bytes | 최소값 | 최대값 | 비고 | +==================+===========+=================================+=====================================+=======================================================================+ | **DATE** | 4 | 0001년 1월 1일 | 9999년 12월 31일 | 예외적으로 DATE '0000-00-00'을 입력할 수 있다. | +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ | **TIME** | 4 | 00시 00분 00초 | 23시 59분 59초 | | +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ | **TIMESTAMP** | 4 | 1970년 1월 1일 0시 0분 1초(GMT) | 2038년 1월 19일 3시 14분 7초(GMT) | 예외적으로 TIMESTAMP '0000-00-00 00:00:00'을 입력할 수 있다. | | | | 1970년 1월 1일 9시 0분 1초(KST) | 2038년 1월 19일 12시 14분 7초(KST) | | +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ | **DATETIME** | 8 | 0001년 1월 1일 0시 0분 0.000초 | 9999년 12월 31일 23시 59분 59.999초 | 예외적으로 DATETIME '0000-00-00 00:00:00'을 입력할 수 있다. | +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ | **TIMESTAMPLTZ** | 4 | 1970년 1월1일 0시 0분 1초(GMT) | 2038년1월19일3시14분7초(GMT) | 로컬 타임존의 TIMESTAMP. | | | | 타임존에 따라 다름 | 타임존에 따라 다름 | 예외적으로, TIMESTAMPLTZ'0000-00-00 00:00:00' 형태가 허용된다. | +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ | **TIMESTAMPTZ** | 8 | 1970년 1월1일 0시 0분 1초(GMT) | 2038년1월19일3시14분7초(GMT) | 로컬 타임존의 TIMESTAMP. | | | | 타임존에 따라 다름 | 타임존에 따라 다름 | 예외적으로, TIMESTAMPTZ '0000-00-00 00:00:00' 형태가 허용된다. | +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ | **DATETIMELTZ** | 8 | 0001년 1월1일 0시0분 0.000초 UTC| 9999년12월31일23시59분59.999초 | 로컬 타임존의 DATETIME. | | | | 타임존에 따라 다름 | 타임존에 따라 다름 | 예외적으로, DATETIMELTZ '0000-00-00 00:00:00' 형태가 허용된다. | +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ | **DATETIMETZ** | 12 | 0001년 1월1일 0시0분 0.000초 UTC| 9999년12월31일23시59분59.999초 | 타임존의 DATETIME. | | | | 타임존에 따라 다름 | 타임존에 따라 다름 | 예외적으로, DATETIMETZ '0000-00-00 00:00:00' 형태가 허용된다. | +------------------+-----------+---------------------------------+-------------------------------------+-----------------------------------------------------------------------+ **범위와 해상도(Range and Resolution)** * 시간 값의 표현은 기본적으로 24시간 시스템에 의하여 그 범위가 결정된다. 날짜는 그레고리력(Gregorian calendar)을 따른다. 이 두 제약 사항을 벗어나는 값이 날짜나 시간으로 입력되면 오류가 발생한다. * **DATE** 중 연도의 범위는 0001~9999 AD이다. * CUBRID 2008 R3.0 버전부터는 연도를 두 자리만 표기하면, 00~69는 2000~2069로 변환되고, 70~99는 1970~1999로 변환된다. R3.0 미만 버전에서는 01~99까지의 두 자리 연도를 표기하면, 각각 0001~0099로 변환된다. * **TIMESTAMP** 의 범위는 GMT로 1970년 1월 1일 0시 0분 1초부터 2038년 1월 19일 03시 14분 07초까지이다. KST (GMT+9)로는 1970년 1월 1일 9시 0분 1초부터 2038년 1월 19일 12시 14분 07초까지 저장할 수 있다. GMT로 timestamp'1970-01-01 00:00:00'은 timestamp'0000-00-00 00:00:00'와 같다. * **TIMESTAMPLTZ**, **TIMESTAMPTZ** 범위는 타임존에 따라 다르지만 UTC로 변환되는 값은 1970-01-01 00:00:01과 2038-01-19 03 03:14:07 사이여야 한다. * **DATETIMELTZ**, **DATETIMETZ** 범위는 타임존에 따라 다르지만 UTC로 변환되는 값은 0001-01-01 00:00:0.000과 9999-12-31 23:59:59.999 사이여야 한다. 세션 타임존이 변경되면 데이터베이스에 저장된 값이 더 이상 유효하지 않다. * 날짜, 시간, 타임스탬프와 관련된 연산은 시스템의 반올림 시스템에 따라 결과가 달라질 수 있다. 이러한 경우, 시간과 타임스탬프는 가장 근접한 초를 최소 해상도로, 날짜는 가장 근접한 날짜를 최소 해상도로 하여 결정된다. **변환(Coercion)** 날짜/시간 데이터 타입의 값은 서로 똑같은 항목을 가지고 있는 경우에만 **CAST** 연산자를 이용한 명시적인 변환이 가능하며, 묵시적 변환은 :ref:`implicit-type-conversion` 을 참고한다. 아래의 표는 명시적 변환이 가능한 타입을 설명한다. 날짜/시간 데이터 타입 간 산술 연산에 대한 내용은 :ref:`arithmetic-op-type-casting` 을 참고한다. **명시적 변환** +----------------+------+------+----------+-----------+ | FROM \\ TO | DATE | TIME | DATETIME | TIMESTAMP | +================+======+======+==========+===========+ | **DATE** | \- | X | O | O | +----------------+------+------+----------+-----------+ | **TIME** | X | \- | X | X | +----------------+------+------+----------+-----------+ | **DATETIME** | O | O | \- | O | +----------------+------+------+----------+-----------+ | **TIMESTAMP** | O | O | O | \- | +----------------+------+------+----------+-----------+ **DATE**, **DATETIME**, **TIMESTAMP** 타입의 연, 월, 일에는 0을 입력할 수 없으나, 예외적으로 날짜와 시간이 모두 0인 값은 허용한다. 해당 타입의 칼럼에 대한 질의 수행 시 인덱스가 있으면 이 값을 사용할 수 있다는 점에서 **NULL** 대신 사용하면 유용하다. * **DATE**, **DATETIME**, **TIMESTAMP** 타입이 인자인 일부 함수는 인자의 날짜와 시간 값이 모두 0이면 시스템 파라미터 **return_null_on_function_errors** 의 값에 따라 다른 값을 반환한다. **return_null_on_function_errors** 가 yes이면 **NULL** 을 반환하고 no이면 에러를 반환하며, 기본값은 **no** 이다. * **DATE**, **DATETIME**, **TIMESTAMP** 타입을 반환하는 함수들은 날짜와 시간 값이 모두 0인 값을 반환할 수 있지만 JAVA 응용 프로그램에서는 이러한 값을 Date 객체에 저장할 수 없다. 따라서 연결 URL 문자열의 zeroDateTimeBehavior 속성(Property) 설정에 따라서 예외로 처리하거나 **NULL**\ 을 반환하거나 또는 최소값을 반환한다(이에 관한 자세한 내용은 :ref:`jdbc-connection-conf` 참고). * 시스템 파라미터 **intl_date_lang**\ 을 설정하면 :func:`TO_DATE`, :func:`TO_TIIME`, :func:`TO_DATETIME`, :func:`TO_TIMESTAMP`, :func:`DATE_FORMAT`, :func:`TIME_FORMAT`, :func:`TO_CHAR`, :func:`STR_TO_DATE` 함수의 입력 문자열 형식이 해당 로캘의 날짜 형식을 따른다. 자세한 내용은 :ref:`stmt-type-parameters`\ 과 각 함수의 설명을 참고한다. * 타임존이 포함된 타입은 상위 타입과 동일한 변환 규칙을 따른다. .. note:: 날짜/시간 타입 및 타임존이 있는 날짜/시간 타입의 리터럴에 대해서는 :ref:`date-time-literal`\을 참고한다. DATE ---- **DATE** 데이터 타입은 연도(*yyyy*), 월(*mm*), 일(*dd*)을 표현하며, 지원 범위는 '01/01/0001'에서 '12/31/9999'까지이다. 연도는 생략 가능하며, 생략될 경우 현재 시스템의 연도 값이 자동으로 지정된다. 입력 형식은 다음과 같다. :: date'mm/dd[/yyyy]' date'[yyyy-]mm-dd' * 모든 항목은 정수 형태로 입력되어야 한다. * CSQL은 '*MM*/*DD*/*YYYY*' 형식으로 날짜 값을 출력하고, JDBC 응용 프로그램 및 CUBRID 매니저는 '*YYYY*-*MM*-*DD*' 형식으로 날짜 값을 출력한다. * 문자열 타입의 데이터를 **DATE** 타입으로 변환하는 함수는 :func:`TO_DATE` 이다. * 연, 월, 일에는 0을 입력할 수 없으나 예외적으로 연, 월, 일이 모두 0인 '0000-00-00'은 입력할 수 있다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: DATE'2008-10-31'은 '10/31/2008'로 출력된다. DATE'10/31'은 '10/31/2011'으로 출력된다(연도가 생략되면 현재 연도가 자동으로 지정됨). DATE'00-10-31'은 '10/31/2000'로 출력된다. DATE'0000-10-31'은 에러가 출력된다(연도의 최소값은 1). DATE'70-10-31'은 '10/31/1970'로 출력된다. DATE'0070-10-31'은 '10/31/0070'로 출력된다. TIME ---- **TIME** 데이터 타입은 시각(*hh*), 분(*mi*), 초(*ss*) 를 표현하며, 지원 범위는 '00:00:00'에서 '23:59:59'까지이다. 초는 생략 가능하며, 생략될 경우 0초로 지정된다. 입력 형식은 12시간 표기법(AM/PM표기법) 또는 24시간 표기법이 모두 허용되며, 다음과 같이 작성한다. :: time'hh:mi[:ss] [am | pm]' * 모든 항목은 정수로 입력되어야 한다. * CSQL은 항상 AM/PM 표기법으로 시간 값을 출력하고, JDBC 응용 프로그램 및 CUBRID 매니저는 24시간 표기법으로 시간 값을 출력한다. * 24시간 표기법으로 시간 값을 입력할 때에도 AM/PM을 지정할 수 있으며, 이때 시간 값과 지정된 AM 또는 PM이 일치하지 않으면 오류가 발생한다. * 모든 시간 값은 데이터베이스에는 24시간 표기법으로 저장된다. * 문자열 타입의 데이터를 **TIME** 타입으로 변환하는 함수는 :func:`TO_TIME` 이다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: TIME'00:00:00'은 '12:00:00 AM'으로 출력된다. TIME'1:15'는 '01:15:00 AM'으로 간주된다. TIME'13:15:45'는 '01:15:45 PM'으로 간주된다. TIME'13:15:45 pm'은 정상적으로 저장된다. TIME'13:15:45 am'은 오류가 발생한다(주어진 시간 값과 AM/PM이 불일치). TIMESTAMP --------- **TIMESTAMP** 데이터 타입은 날짜(연, 월, 일)와 시간(시, 분, 초)을 결합한 데이터 값을 표현하며, GMT로 '1970-01-01 00:00:01'부터 '2038-01-19 03:14:07'까지 표현할 수 있다. 이 범위를 초과하거나 밀리초 단위의 시간 데이터를 저장하는 경우라면, **DATETIME** 데이터 타입을 이용할 수 있다. **TIMESTAMP** 데이터 타입의 입력 형식은 다음과 같다. :: timestamp'hh:mi[:ss] [am|pm] mm/dd[/yyyy]' timestamp'hh:mi[:ss] [am|pm] [yyyy-]mm-dd' timestamp'mm/dd[/yyyy] hh:mi[:ss] [am|pm]' timestamp'[yyyy-]mm-dd hh:mi[:ss] [am|pm]' * 모든 항목은 정수로 입력되어야 한다. * 연도를 생략하면 기본값으로 현재 연도가 지정되고, 시간 값(시/분/초)를 생략하면 12:00:00 AM으로 지정된다. * 시스템의 현재 타임스탬프 값은 :c:macro:`SYS_TIMESTAMP` (또는 :c:macro:`SYSTIMESTAMP`, :c:macro:`CURRENT_TIMESTAMP`) 함수를 이용하여 **TIMESTAMP** 데이터 타입에 저장할 수 있다. * :func:`TIMESTAMP` 함수 또는 :func:`TO_TIMESTAMP` 함수를 사용하면, 문자열 데이터 타입의 데이터를 **TIMESTAMP** 데이터 타입으로 변환할 수 있다. * 연, 월, 일에는 0을 입력할 수 없으나 예외적으로 연, 월, 일, 시, 분, 초가 모두 0인 '0000-00-00 00:00:00'은 입력할 수 있다. GMT timestamp'1970-01-01 12:00:00 AM' 또는 KST timestamp'1970-01-01 09:00:00 AM'은 timestamp'0000-00-00 00:00:00'으로 해석된다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: TIMESTAMP'10/31'은 '12:00:00 AM 10/31/2011'으로 출력된다(연도/시간이 생략될 경우, 기본값으로 출력). TIMESTAMP'10/31/2008'은 '12:00:00 AM 10/31/2008'로 출력된다(시간이 생략될 경우, 기본값으로 출력). TIMESTAMP'13:15:45 10/31/2008'은 '01:15:45 PM 10/31/2008'로 출력된다. TIMESTAMP'01:15:45 PM 2008-10-31'은 '01:15:45 PM 10/31/2008'로 출력된다. TIMESTAMP'13:15:45 2008-10-31'은 '01:15:45 PM 10/31/2008'로 출력된다. TIMESTAMP'10/31/2008 01:15:45 PM'은 '01:15:45 PM 10/31/2008'로 출력된다. TIMESTAMP'10/31/2008 13:15:45'는 '01:15:45 PM 10/31/2008'로 출력된다. TIMESTAMP'2008-10-31 01:15:45 PM'은 '01:15:45 PM 10/31/2008'로 출력된다. TIMESTAMP'2008-10-31 13:15:45'는 '01:15:45 PM 10/31/2008'로 출력된다. TIMESTAMP'2099-10-31 01:15:45 PM'은 오류가 발생한다(TIMESTAMP 표현 가능 범위 초과). DATETIME -------- **DATETIME** 타입은 날짜(년, 월, 일)와 시간(시, 분, 초, 밀리초)을 결합한 데이터 값을 표현하며, GMT로 0001-01-01 00:00:00.000부터 9999-12-31 23:59:59.999까지 표현할 수 있다. **DATETIME** 타입 데이터의 입력 형식은 다음과 같다. :: datetime'hh:mi[:ss[.msec]] [am|pm] mm/dd[/yyyy]' datetime'hh:mi[:ss[.msec]] [am|pm] [yyyy-]mm-dd' datetime'mm/dd[/yyyy] hh:mi[:ss[.ff]] [am|pm]' datetime'[yyyy-]mm-dd hh:mi[:ss[.ff]] [am|pm]' * 모든 항목은 정수로 입력되어야 한다. * 연도를 생략하면 기본값으로 현재 연도가 지정되고, 시간 값(시/분/초/밀리초)를 생략하면 12:00:00.000 AM으로 지정된다. * 시스템의 현재 타임스탬프 값은 :c:macro:`SYS_DATETIME` (또는 :c:macro:`SYSDATETIME`, :c:macro:`CURRENT_DATETIME`, :func:`CURRENT_DATETIME`, :func:`NOW`)를 이용하여 **DATETIME** 타입에 저장할 수 있다. * 문자열 타입의 데이터를 **DATETIME** 타입으로 변환하는 함수는 :func:`TO_DATETIME` 이다. * 연, 월, 일에는 0을 입력할 수 없으나 예외적으로 연, 월, 일, 시, 분, 초가 모두 0인 '0000-00-00 00:00:00'은 입력할 수 있다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. :: DATETIME'10/31'은 '12:00:00.000 AM 10/31/2011'으로 출력된다(연도/시간이 생략될 경우, 기본값으로 출력). DATETIME'10/31/2008'은 '12:00:00.000 AM 10/31/2008'로 출력된다. DATETIME'13:15:45 10/31/2008'은 '01:15:45.000 PM 10/31/2008'로 출력된다. DATETIME'01:15:45 PM 2008-10-31'은 '01:15:45.000 PM 10/31/2008'로 출력된다. DATETIME'13:15:45 2008-10-31'은 '01:15:45.000 PM 10/31/2008'로 출력된다. DATETIME'10/31/2008 01:15:45 PM'은 '01:15:45.000 PM 10/31/2008'로 출력된다. DATETIME'10/31/2008 13:15:45'는 '01:15:45.000 PM 10/31/2008'로 출력된다. DATETIME'2008-10-31 01:15:45 PM'은 '01:15:45.000 PM 10/31/2008'로 출력된다. DATETIME'2008-10-31 13:15:45'는 '01:15:45.000 PM 10/31/2008'로 출력된다. DATETIME'2099-10-31 01:15:45 PM'은 '01:15:45.000 PM 10/31/2099'로 출력된다. .. _cast-string-to-datetime: 문자열을 날짜/시간 타입으로 CAST -------------------------------- .. _cast-to-datetime-recommend: 날짜/시간 타입 문자열 권장 형식 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :func:`CAST` 연산자를 사용하여 문자열을 날짜/시간 타입으로 변환할 때에는 문자열을 다음과 같은 형식으로 작성하는 것을 권장한다. 참고로, :func:`CAST` 연산자에서 사용하는 날짜/시간 문자열 형식은 DB 생성 시 지정하는 로캘의 영향을 받지 않는다. 또한, :func:`TO_DATE`, :func:`TO_TIME`, :func:`TO_DATETIME`, :func:`TO_TIMESTAMP` 함수에서 문자열에 대한 날짜/시간 형식을 생략하는 경우에도 날짜/시간 문자열 형식을 아래와 같이 작성한다. * **DATE** 타입 :: YYYY-MM-DD MM/DD/YYYY * **TIME** 타입 :: HH:MI:SS [AM|PM] * **DATETIME** 타입 :: YYYY-MM-DD HH:MI:SS[.msec] [AM|PM] HH:MI:SS[.msec] [AM|PM] YYYY-MM-DD MM/DD/YYYY HH:MI:SS[.msec] [AM|PM] HH:MI:SS[.msec] [AM|PM] MM/DD/YYYY * **TIMESTAMP** 타입 :: YYYY-MM-DD HH:MI:SS [AM|PM] HH:MI:SS [AM|PM] YYYY-MM-DD MM/DD/YYYY HH:MI:SS [AM|PM] HH:MI:SS [AM|PM] MM/DD/YYYY 날짜/시간 타입 문자열 허용 형식 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :func:`CAST` 연산자는 날짜/시간 문자열에 대해 아래와 같은 형식을 허용한다. **DATE 문자열 허용 형식** :: [year sep] month sep day * 2011-04-20 : 2011년 4월 20일 * 04-20 : 올해 4월 20일 구분자(*sep*)가 빗금(/)일 때에는 다음과 같은 순서로 인식한다. :: month/day[/year] * 04/20/2011 : 2011년 4월 20일 * 04/20 : 올해 4월 20일 구분자(*sep*)를 사용하지 않을 때에는 다음과 같은 형식으로 인식한다. 연도는 한 자리, 두 자리, 네 자리를 허용하고, 월은 한 자리, 두 자리를 허용한다. 일은 항상 두 자리를 입력해야 한다. :: YYYYMMDD YYMMDD YMMDD MMDD MDD * 20110420 : 2011년 4월 20일 * 110420 : 2011년 4월 20일 * 420 : 올해 4월 20일 **TIME 문자열 허용 형식** :: [hour]:min[:[sec]][.[msec]] [am|pm] * 09:10:15.359 am : 오전 9시 10분 15초(0.359초는 버림) * 09:10:15 : 오전 9시 10분 15초 * 09:10 : 오전 9시 10분 * \:10 : 오전 12시 10분 :: [[[[[[Y]Y]Y]Y]M]MDD]HHMISS[.[msec]] [am|pm] * 20110420091015.359 am : 오전 9시 10분 15초 * 0420091015 : 오전 9시 10분 15초 :: [H]HMMSS[.[msec]] [am|pm] * 091015.359 am : 오전 9시 10분 15초 * 91015 : 오전 9시 10분 15초 :: [M]MSS[.[msec]] [am|pm] * 1015.359 am : 오전 12시 10분 15초 * 1015 : 오전 12시 10분 15초 :: [S]S[.[msec]] [am|pm] * 15.359 am : 오전 12시 0분 15초 * 15 : 오전 12시 0분 15초 .. note:: CUBRID 2008 R3.1 이하 버전에서는 [H]H 형식을 허용했다. 즉 R3.1 이하 버전에서 문자열 '10'은 **TIME**'10:00:00'으로 변환되었으나, R4.0부터는 **TIME**'00:00:10' 으로 변환된다. **DATETIME 문자열 허용 형식** :: [year sep] month sep day [sep] [sep] hour [sep min[sep sec[.[msec]]]] * 04-20 09 : 올해 4월 20일 오전 9시 :: month/day[/year] [sep] hour [sep min [sep sec[.[msec]]]] * 04/20 09 : 올해 4월 20일 오전 9시 :: year sep month sep day sep hour [sep min[sep sec[.[msec]]]] * 2011-04-20 09 : 2011년 4월 20일 오전 9시 :: month/day/year sep hour [sep min[sep sec [.[msec]]]] * 04/20/2011 09 : 2011년 4월 20일 오전 9시 :: YYMMDDH (시간이 한 자리 수일 때에만 허용) * 1104209 : 2011년 4월 20일 오전 9시 :: YYMMDDHHMI[SS[.msec]] * 1104200910.359 : 2011년 4월 20일 오전 9시 10분(0.359초는 버림) * 110420091000.359 : 2011년 4월 20일 오전 9시 10분 0.359초 :: YYYYMMDDHHMISS[.msec] * 201104200910.359 : 2020년 11월 4일 오후 8시 9분 10.359초 * 20110420091000.359 : 2011년 4월 20일 오전 9시 10분 0.359초 **시간-날짜 순서의 문자열 허용 형식** :: [hour]:min[:sec[.msec]] [am|pm] [year-]month-day * 09:10:15.359 am 2011-04-20 : 2011년 4월 20일 오전 9시 10분 15.359초 * \:10 04-20 : 올해 4월 20일 오전 12시 10분 :: [hour]:min[:sec[.msec]] [am|pm] month/day[/[year]] * 09:10:15.359 am 04/20/2011 : 2011년 4월 20일 오전 9시 10분 15.359초 * \:10 04/20 : 올해 4월 20일 오전 12시 10분 :: hour[:min[:sec[.[msec]]]] [am|pm] [year-]month-day * 09:10:15.359 am 04-20 : 올해 4월 20일 오전 9시 10분 15.359초 * 09 04-20 : 올해 4월 20일 오전 9시 :: hour[:min[:sec[.[msec]]]] [am|pm] month/day[/[year]] * 09:10:15.359 am 04/20 : 올해 4월 20일 오전 9시 10분 15.359초 * 09 04/20 : 올해 4월 20일 오전 9시 **규칙** *msec*\은 밀리초를 나타내는 일련의 숫자이다. 앞에서 네 번째 자리부터 이후의 숫자는 무시된다. 값 사이를 구분하는 구분자의 규칙은 다음과 같다. * **TIME** 문자열은 시간 구분자로 항상 하나의 콜론(:)을 사용해야 한다. * **DATE** 와 **DATETIME** 문자열은 구분자 없이 연속된 숫자로 나타낼 수 있고, **DATETIME** 문자열은 시간과 날짜를 공백으로 구분할 수 있다. * 입력 문자열 안에서 구분자들은 동일해야 한다. * 시간-날짜 순서의 문자열은 시간 구분자로 콜론(:)만 사용할 수 있으며, 날짜 구분자로는 하이픈(-)이나 빗금(/)만 사용할 수 있다. 날짜 입력 시 하이픈을 사용하는 경우 yyyy-mm-dd 순으로 입력하며, 빗금(/)을 사용하는 경우 mm/dd/yyyy 순으로 입력한다. 날짜 부분의 문자열에는 다음 규칙이 적용된다. * 연도는 구문이 허용하는 한 생략할 수 있다. * 연도를 두 자리로 입력하면 1970년~2069년 범위의 연도를 나타낸다. 즉, YY<70 이면 2000+YY으로 처리하고, YY>=70이면 1900+YY으로 처리한다. 한 자리나 세 자리, 네 자리 숫자로 연도를 입력하면 해당 숫자 그대로를 나타낸다. * 문자열 앞뒤의 공백과 뒤의 문자열은 무시된다. **DATETIME**, **TIME** 문자열을 위한 am/pm 지정자는 시간 값의 일부로 인식하지만, 공백이 아닌 문자가 뒤에 붙으면 am/pm 지정자로 인식되지 않는다. CUBRID의 **TIMESTAMP** 타입은 **DATE** 타입과 **TIME** 타입으로 구성되고, **DATETIME** 타입은 **DATE** 타입과 **TIME** 타입에 밀리초(milliseconds)가 더해져서 구성된다. 입력 문자열은 날짜(**DATE** 문자열), 시간(**TIME** 문자열), 혹은 둘 다(**DATETIME** 문자열) 포함할 수 있다. 특정 타입의 데이터를 보유한 문자열은 다른 타입으로도 변환될 수 있으며 다음과 같은 규칙이 적용된다. * **DATE** 문자열을 **DATETIME** 타입으로 변환하면 시간 값은 '00:00:00'이 된다. * **TIME** 문자열을 **DATETIME** 타입으로 변환하면 콜론(:)이 날짜 구분자로 인식되어 **TIME** 문자열이 날짜를 나타내는 문자열로 인식되고, 시간 값은 '00:00:00'이 된다. * **DATETIME** 문자열을 **DATE** 타입으로 변환하면 결과값에서 시간 부분은 무시되지만, 시간 입력값의 형식은 유효해야 한다. * **DATETIME** 문자열을 **TIME** 타입으로 변환할 수 있지만, 다음과 같은 규칙이 적용된다. * 문자열에 있는 날짜와 시간은 최소한 하나의 공백에 의해 구분되어야 한다. * 결과값에서 날짜 부분은 무시되지만, 날짜 입력값의 형식이 유효해야 한다. * 날짜 부분의 연도가 4자리 이상이거나(0으로 시작할 수 있음), 시간 부분이 최소한 시와 분([H]H:[M]M)을 포함해야 한다. 그렇지 않으면 날짜 부분이 [MM]SS 포맷의 **TIME** 타입으로 인식되고, 뒤이어 나오는 문자열은 무시된다. * **DATETIME** 문자열의 각 단위(년, 월, 일, 시, 분, 초) 중 하나가 999999보다 크면, 숫자가 아닌 것으로 인식하여 해당 단위가 포함된 문자열이 무시된다. 예를 들어 '2009-10-21 20:9943:10'은 분 단위의 값이 범위를 벗어나므로 에러가 발생한다. 그러나 '2009-10-21 20:1000123:10'이 입력되면 '2009'를 MMSS 포맷의 **TIME** 타입으로 인식하여 **TIME**'00:20:09'를 반환한다. * 시간-날짜 순서의 문자열을 **TIME** 타입으로 변환하면 문자열의 날짜 부분은 무시되지만, 날짜 부분의 형식은 유효해야 한다. * 시간 부분이 있는 모든 입력 문자열은 변환 시 [*msec*] 을 허용하지만, **DATETIME** 타입만 그 값을 유지한다. **DATE**, **TIMESTAMP**, **TIME** 와 같은 타입으로 변환하면 *msec* 값을 버린다. * **DATETIME**, **TIME** 문자열에서의 모든 변환은 시간 값 뒤에 나오는 영문 로캘(locale) 또는 서버의 현재 로캘로 쓰여진 am/pm 지정자를 허용한다. .. code-block:: sql SELECT CAST('420' AS DATE); :: cast('420' as date) ====================== 04/20/2012 .. code-block:: sql SELECT CAST('91015' AS TIME); :: cast('91015' as time) ======================== 09:10:15 AM .. code-block:: sql SELECT CAST('110420091035.359' AS DATETIME); :: cast('110420091035.359' as datetime) ======================================= 09:10:35.359 AM 04/20/2011 .. code-block:: sql SELECT CAST('110420091035.359' AS TIMESTAMP); :: cast('110420091035.359' as timestamp) ======================================== 09:10:35 AM 04/20/2011 .. CUBRIDSUS-14182 현재 timeltz가 offset을 허용하려면 timezone 파라미터 값 역시 오프셋으로 설정되어야 함. 버그로 수정 예정(?): 수정 여부에 따라 아래 설명을 바꿀 것. .. _timezone-type: 타임존이 있는 날짜/시간 데이터 타입 =================================== 타임존이 있는 날짜/시간 데이터 타입은 타임존을 명시하여 입력하거나 출력할 수 있는 날짜/시간 타입이다. 타임존을 설정하는 방법은 지역 이름을 명시하는 방법과 시간의 오프셋을 명시하는 방법이 있다. 기존의 날짜/시간 타입 이름 뒤에 TZ 또는 LTZ가 붙어 있는 경우 타임존 정보를 고려하게 되는데, TZ는 타임존을 의미하며, LTZ는 로컬 타임존을 의미한다. * TZ 타입은 WITH TIME ZONE으로도 표현이 가능하다. 내부적으로 UTC 시간과 생성 시 타임존 정보(사용자가 명시하거나 세션 타임존에 의해 결정됨)를 저장한다. TZ 타입은 타임존을 저장하기 위해 4바이트가 추가로 필요하다. * LTZ 타입은 WITH LOCAL TIME ZONE으로도 표현이 가능하다. 내부적으로 UTC 시간을 저장하며, 출력 시 로컬(현재의 세션) 타임존으로 변환된다. 타임존이 없는 타입과 비교하기 위해, 다음 표에는 타임존이 없는 타입과 타임존이 있는 타입을 함께 설명하였다. 표의 설명에 있는 UTC는 협정 세계시(Coordinated Universal Time)를 나타낸다. +-----------+----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ | 구분 | 타입 | 입력 | 저장 | 출력 | 설명 | +===========+================+=========================+===================================+============================================+=================================================================+ | DATE | DATE | 타임존 비포함 | 입력 값 | 절대 값(입력 값과 동일) | 날짜 | +-----------+----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ | DATETIME | DATETIME | 타임존 비포함 | 입력 값 | 절대 값(입력 값과 동일) | 밀리초를 포함한 날짜/시간 | | +----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ | | DATETIMETZ | 타임존 포함 | UTC + 타임존(지역 또는 오프셋) | 절대 값(입력한 타임존 유지) | 날짜/시간 + 타임존 정보 | | +----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ | | DATETIMELTZ | 타임존 포함 | UTC | 상대 값(세션 타임존에 따라 변환됨) | 세션 타임존에서의 날짜/시간 | +-----------+----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ | TIME | TIME | 타임존 비포함 | 입력 값 | 절대 값(입력 값과 동일) | 시간 | +-----------+----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ | TIMESTAMP | TIMESTAMP | 타임존 비포함 | UTC | 상대 값(세션 타임 존에 따라 변환됨) | 입력 값을 세션 타임존의 값으로 해석함 | | +----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ | | TIMESTAMPTZ | 타임존 포함 | UTC + 타임존(지역 또는 오프셋) | 절대 값(입력한 타임존 유지) | UTC + 타임존이 있는 타임스탬프 | | +----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ | | TIMESTAMPLTZ | 타임존 포함 | UTC | 상대 값(세션 타임존에 따라 변환됨) | 세션 타임존. TIMESTAMP의 값과 같음. | | | | | | | 출력할 때 타임존 지정자를 포함함 | +-----------+----------------+-------------------------+-----------------------------------+--------------------------------------------+-----------------------------------------------------------------+ 타임존이 있는 날짜/시간 타입의 최대값, 최소값, 범위와 해상도 등 나머지 특징들은 일반적인 날짜/시간 타입의 특징과 동일하다. .. note:: * CUBRID에서, TIMESTAMP가 1970년 1월 1일 UTC 이후 경과된 '초'로 보관된다(UNIX 시간). * 타 DBMS의 TIMESTAMP는 CUBRID 의 DATETIME 과 비슷한 방식이며 'milliseconds'를 보관한다. 타임존 타입을 사용하는 함수의 예를 보려면 다음을 참고한다. :doc:`function/datetime_fn` 다음은 세션 타임존의 변경에 따라 DATETIME, DATETIMETZ와 DATETIMELTZ의 출력 값이 다르게 나타나는 예이다. .. code-block:: sql -- csql> ;set timezone="+09" CREATE TABLE tbl (a DATETIME, b DATETIMETZ, c DATETIMELTZ); INSERT INTO tbl VALUES (datetime'2015-02-24 12:30', datetimetz'2015-02-24 12:30', datetimeltz'2015-02-24 12:30'); SELECT * FROM tbl :: 12:30:00.000 PM 02/24/2015 12:30:00.000 PM 02/24/2015 +09:00 12:30:00.000 PM 02/24/2015 +09:00 .. code-block:: sql -- csql> ;set timezone="+07" SELECT * FROM tbl; :: 12:30:00.000 PM 02/24/2015 12:30:00.000 PM 02/24/2015 +09:00 10:30:00.000 AM 02/24/2015 +07:00 다음은 세션 타임존의 변경에 따라 TIMESTAMP, TIMESTAMPTZ와 TIMESTAMPLTZ의 출력 값이 다르게 나타나는 예이다. .. code-block:: sql -- ;set timezone="+09" CREATE TABLE tbl (a TIMESTAMP, b TIMESTAMPTZ, c TIMESTAMPLTZ); INSERT INTO tbl VALUES (timestamp'2015-02-24 12:30', timestamptz'2015-02-24 12:30', timestampltz'2015-02-24 12:30'); SELECT * FROM tbl; :: 12:30:00 PM 02/24/2015 12:30:00 PM 02/24/2015 +09:00 12:30:00 PM 02/24/2015 +09:00 .. code-block:: sql -- csql> ;set timezone="+07" SELECT * FROM tbl; :: 10:30:00 AM 02/24/2015 12:30:00 PM 02/24/2015 +09:00 10:30:00 AM 02/24/2015 +07:00 **문자열을 TIMESTAMP 타입으로 변환** 문자열을 timestamp/timestampltz/timestamptz로 변환하는 작업은 문자열로부터 TIMESTAMP 객체를 생성하는 과정에서 수행된다. +----------------------------+-----------------------------+----------------------------+------------------------------+ | From/to | Timestamp | Timestampltz | Timestamptz | +============================+=============================+============================+==============================+ | String (타임존 생략) | 세션 타임존으로 | 세션 타임존으로 | 세션 타임존으로 | | | 날짜/시간을 UTC로 변환하고 | 날짜/시간을 UTC로 변환하고 | 날짜/시간을 UTC로 변환하고 | | | Unix 시간으로 인코딩하여 | Unix 시간으로 인코딩하여 | Unix 시간과 세션의 TZ_ID로 | | | 저장 | 저장 | 인코딩하여 저장 | | | | | | +----------------------------+-----------------------------+----------------------------+------------------------------+ | String (타임존 포함) | 오류 (timestamp 에서는 | 문자열의 타임존으로 | 문자열의 타임존으로 | | | 타임존을 허용 하지 않음) | 날짜/시간을 UTC로 변환하고 | 날짜/시간을 UTC로 변환하고 | | | | Unix 시간으로 인코딩하여 | Unix 시간과 세션의 TZ_ID로 | | | | 저장 | 인코딩하여 저장 | | | | | | +----------------------------+-----------------------------+----------------------------+------------------------------+ **문자열을 DATETIME 타입으로 변환** 문자열을 datetime/datetimeltz/datetimetz로 변환하는 작업은 문자열로부터 DATETIME 객체를 생성하는 과정에서 수행된다. +----------------------------+-----------------------------+----------------------------+------------------------------+ | From/to | Datetime | Datetimeltz | Datetimetz | +============================+=============================+============================+==============================+ | String (타임존 생략) | 문자열에서 분석된 | 세션 타임존으로 | 세션 타임존으로 | | | 값을 저장 | 날짜/시간을 UTC로 변환하고 | 날짜/시간을 UTC로 변환하고 | | | | 새로운 값들로 저장 | 새로운 값과 세션의 TZ_ID로 | | | | | 저장 | | | | | | +----------------------------+-----------------------------+----------------------------+------------------------------+ | String (타임존 포함) | 오류 (datetime 에서는 | 문자열의 타임존으로 | 문자열의 타임존으로 | | | 타임존을 허용 하지 않음) | 날짜/시간을 UTC로 변환하고 | 날짜/시간을 UTC로 변환하고 | | | | 새로운 값들로 저장 | 새로운 값과 세션의 TZ_ID로 | | | | | 저장 | | | | | | +----------------------------+-----------------------------+----------------------------+------------------------------+ **DATETIME 및 TIMESTAMP 타입을 문자열로 변환** +----------------------------+-----------------------------+----------------------------+------------------------------+ | From/to | String (타임존 출력 불허) | String (타임존 | String (타임존 비요청 - 자동 | | | | 강제출력) | 선택) | +============================+=============================+============================+==============================+ | TIMESTAMP | 세션 타임존으로 Unix 시간을 | 세션 타임존으로 Unix 시간을| 세션 타임존으로 Unix 시간을 | | | 디코딩한 후 출력 | 디코딩한 후 세션 타임존 | 디코딩한 후 출력. 세션 타임존| | | | 문자열과 함께 출력 | 문자열은 출력하지 않음 | +----------------------------+-----------------------------+----------------------------+------------------------------+ | TIMESTAMPLTZ | 세션 타임존으로 Unix 시간을 | 세션 타임존으로 Unix 시간을| 세션 타임존으로 Unix 시간을 | | | 디코딩한 후 출력 | 디코딩한 후 세션 타임존 | 디코딩한 후 출력. | | | | 문자열과 함께 출력 | 세션 타임존 문자열 출력 | +----------------------------+-----------------------------+----------------------------+------------------------------+ | TIMESTAMPTZ | 값의 타임존으로 Unix 시간을 | 값의 타임존으로 Unix 시간을| 값의 타임존으로 Unix 시간을 | | | 디코딩한 후 출력 | 디코딩한 후 | 디코딩한 후 출력. | | | | 값의 타임존 문자열과 함께 | | | | | 출력 | 값의 타임존 문자열 출력 | +----------------------------+-----------------------------+----------------------------+------------------------------+ | DATETIME | 저장된 값을 출력 | 세션 타임존 문자열과 함께 | 저장된 값을 출력. | | | | 저장된 값을 출력 | 타임존 문자열은 출력하지 않음| +----------------------------+-----------------------------+----------------------------+------------------------------+ | DATETIMELTZ | 세션 타임존으로 UTC 값을 | 세션 타임존으로 UTC 값을 | 세션 타임존으로 UTC 값을 | | | 변환후 출력 | 변환 후 세션 타임존 | 변환후 출력. | | | | 문자열과 함께 출력 | 세션 타임존 문자열 출력 | +----------------------------+-----------------------------+----------------------------+------------------------------+ | DATETIMETZ | 값의 타임존으로 UTC 값을 | 값의 타임존으로 UTC 값을 | 값의 타임존으로 UTC 값을 | | | 변환후 출력 | 변환 후 값의 타임존 | 변환후 출력. | | | | 문자열과 함께 출력 | 값의 타임존 문자열 출력 | +----------------------------+-----------------------------+----------------------------+------------------------------+ 타임존 설정 ----------- 다음은 cubrid.conf 파일에서 설정하는 타임존 관련 파라미터들이다. 파라미터의 설정과 관련해서는 :ref:`cubrid-conf`\를 참고한다. * **timezone** 세션에 대한 타임존을 설정하며, 기본값은 **server_timezone**\의 값이다. * **server_timezone** 서버에 대한 타임존을 설정하며, 기본값은 OS의 타임존이다. * **tz_leap_second_support** 윤초(leap second)에 대한 지원 여부를 yes 또는 no로 설정하며, 기본값은 no이다. 타임존 함수 ----------- 다음은 타임존과 관련된 함수들이다. 설명을 보려면 각 함수의 이름을 클릭한다. * :func:`DBTIMEZONE` * :func:`SESSIONTIMEZONE` * :func:`FROM_TZ` * :func:`NEW_TIME` * :func:`TZ_OFFSET` 타임존 타입을 사용하는 함수 --------------------------- DATETIME, TIMESTAMP, TIME 타입의 값을 입력 값으로 사용하는 함수들은 모두 타임존 타입을 사용할 수 있다. 다음은 타임존 타입 값을 사용하는 예인데, 타임존이 없는 경우와 동일하게 동작한다. 다만, LTZ로 끝나는 타입의 경우 출력 값은 로컬 타임존의 설정(timezone 파라미터)을 따른다. 다음 예에서 숫자의 기본 단위는 DATETIME 타입의 최소 단위인 밀리초이다. .. code-block:: sql SELECT datetimeltz '09/01/2009 03:30:30 pm' + 1; :: 03:30:30.001 PM 09/01/2009 Asia/Seoul .. code-block:: sql SELECT datetimeltz '09/01/2009 03:30:30 pm' - 1; :: 03:30:29.999 PM 09/01/2009 Asia/Seoul 다음 예에서 숫자의 기본 단위는 TIMESTAMP 타입의 최소 단위인 초이다. .. code-block:: sql SELECT timestamptz '09/01/2009 03:30:30 pm' + 1; :: 03:30:31 PM 09/01/2009 Asia/Seoul .. code-block:: sql SELECT timestamptz '09/01/2009 03:30:30 pm' - 1; :: 03:30:29 PM 09/01/2009 Asia/Seoul .. code-block:: sql SELECT EXTRACT (hour from datetimetz'10/15/1986 5:45:15.135 am Europe/Bucharest'); 5 이름이 LTZ로 끝나는 타입은 출력 시 로컬 타임존의 설정을 따른다. 따라서 아래 예와 같이 timezone 파라미터의 값이 'Asia/Seoul'로 설정되어 있다면 EXTRACT 함수는 해당 타임존의 시(hour)를 출력한다. .. code-block:: sql -- csql> ;set timezone='Asia/Seoul' SELECT EXTRACT (hour from datetimeltz'10/15/1986 5:45:15.135 am Europe/Bucharest'); :: 12 타임존 타입에 대한 변환 함수 ---------------------------- 다음은 문자열에서 날짜/시간 타입 값으로 변환하거나 반대로 날짜/시간 타입 값에서 문자열로 변환하는 함수들인데, 이들의 입력 값에는 오프셋, 존, 일광 절약과 같은 타임존 정보가 추가될 수 있다. * :func:`DATE_FORMAT` * :func:`STR_TO_DATE` * :func:`TO_CHAR` * :func:`TO_DATETIME_TZ` * :func:`TO_TIMESTAMP_TZ` 각 함수들의 사용 방법은 함수 이름을 클릭하여 해당 함수의 설명을 참고한다. .. code-block:: sql SELECT DATE_FORMAT (datetimetz'2012-02-02 10:10:10 Europe/Zurich CET', '%TZR %TZD %TZH %TZM'); SELECT STR_TO_DATE ('2001-10-11 02:03:04 AM Europe/Bucharest EEST', '%Y-%m-%d %h:%i:%s %p %TZR %TZD'); SELECT TO_CHAR (datetimetz'2001-10-11 02:03:04 AM Europe/Bucharest EEST'); SELECT TO_DATETIME_TZ ('2001-10-11 02:03:04 AM Europe/Bucharest EEST'); SELECT TO_TIMESTAMP_TZ ('2001-10-11 02:03:04 AM Europe/Bucharest'); .. note:: :func:`TO_TIMESTAMP_TZ` , :func:`TO_DATETIME_TZ` 함수는 날짜/시간 인자에 TZR, TZD, TZH 및 TZM 정보를 포함할 수 있다는 것을 제외하고는 :func:`TO_TIMESTAMP` 및 :func:`TO_DATETIME` 함수와 동일하다. 타임존의 지역 이름은 IANA(Internet Assigned Numbers Authority) 타임존 데이터베이스에 있는 지역을 사용하는데, IANA 타임존에 대해서는 http://www.iana.org/time-zones\을 참고한다. IANA 타임존 ----------- IANA(Internet Assigned Numbers Authority) 타임존 데이터베이스에는 수많은 세계 대표 장소에 대한 지역 시간의 역사를 표현하는 코드와 데이터가 들어 있다. 이 데이터베이스는 타임 존 경계, UTC 오프셋, 그리고 일광 절약 규칙에 대해 정치체에 의해 변경된 사항을 반영하기 위해 정기적으로 업데이트되고 있으며, 관리 절차는 `BCP 175: Procedures for Maintaining the Time Zone Database. `\에 설명되어 있다. 자세한 사항은 http://www.iana.org/time-zones\ 를 참고한다. CUBRID는 IANA 타임존을 지원하며, CUBRID 설치 패키지에 포함되어 있는 IANA 타임존 라이브러리를 그대로 사용할 수 있다. 최신 타임존으로 업데이트하고 싶은 경우 타임존 데이터를 업데이트하고, 타임존 라이브러리를 컴파일한 후 데이터베이스를 재구동해야 한다. 이와 관련하여 :ref:`timezone-library`\을 참고한다. 비트열 데이터 타입 ================== 비트열은 0과 1로 이루어진 이진 값의 순열(sequence)이다. CUBRID는 두 가지 비트열을 지원한다. * 고정길이 비트열(**BIT**) * 가변길이 비트열(**BIT VARYING**) 메서드의 인자나 속성의 타입으로 비트열을 사용할 수 있으며, 비트열 리터럴은 2진수 형식이나 16진수 형식을 사용한다. 2진수 형식으로 사용할 때에는 다음과 같이 문자 **B** 뒤에 0과 1로 이루어진 문자열을 붙이거나, **0b** 뒤에 값을 붙여 표현한다. :: B'1010' 0b1010 16진수 형식은 대문자 **X** 뒤에 0-9 그리고 A-F 문자로 이루어진 문자열을 붙이거나 **0x** 뒤에 값을 붙여 표현한다. 예를 들어, 아래는 앞에서 2진수로 표현한 것과 같은 값을 16진수로 나타낸 것이다. :: X'a' 0xA 16진수에서 사용되는 문자는 대소문자를 구분하지 않는다. 즉, X'4f'와 X'4F'는 같은 값으로 간주한다. **길이(Length)** 비트열이 테이블 속성이나 메서드 선언에 사용될 때에는 최대 길이를 표시해야 한다. 비트열이 가질 수 있는 최대 길이는 1,073,741,823비트이다. **비트열의 변환(Bit String Coercion)** 고정길이와 가변길이 비트열 간에는 서로 비교를 위하여 자동 변환이 이루어진다. 명시적인 변환은 **CAST** 연산자를 이용해야 한다. BIT(n) ------ 고정길이 2진수 혹은 16진수 비트열은 **BIT** (*n*)로 나타내는데, 여기서 *n* 은 최대 비트의 개수를 나타낸다. 만약, *n* 이 생략되면 길이는 1로 지정된다. 비트열은 8비트 단위로 왼쪽부터 값이 채워진다. 예를 들어, B'1'의 값은 B'10000000'과 같은 값으로 출력된다. 따라서, 8비트 단위로 길이를 선언하고 8비트 단위로 값을 입력할 것을 권장한다. .. note:: BIT(4)로 선언된 칼럼에 B'1'을 INSERT하면 CSQL에서는 X'8'로 출력되고, CUBRID Manager에서는 X'80'으로 출력된다. * *n* 은 0보다 큰 숫자여야 한다. * 비트열의 크기가 *n* 을 넘어설 경우에는 절삭되고, 0으로 채워진다. * *n* 보다 작은 비트열이 저장될 때에는 나머지 오른쪽 부분이 0으로 채워진다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. .. code-block:: sql CREATE TABLE bit_tbl(a1 BIT, a2 BIT(1), a3 BIT(8), a4 BIT VARYING); INSERT INTO bit_tbl VALUES (B'1', B'1', B'1', B'1'); INSERT INTO bit_tbl VALUES (0b1, 0b1, 0b1, 0b1); INSERT INTO bit_tbl(a3,a4) VALUES (B'1010', B'1010'); INSERT INTO bit_tbl(a3,a4) VALUES (0xaa, 0xaa); SELECT * FROM bit_tbl; :: a1 a2 a3 a4 ========================================================================= X'8' X'8' X'80' X'8' X'8' X'8' X'80' X'8' NULL NULL X'a0' X'a' NULL NULL X'aa' X'aa' BIT VARYING(n) -------------- 가변길이 비트열은 **BIT VARYING** (*n*)으로 나타낸다. 여기서 *n* 은 최대 비트의 개수를 나타낸다. 만약, *n* 이 생략되면 최대 길이인 1,073,741,823으로 지정된다. 비트열은 8비트 단위로 왼쪽부터 값이 채워진다. 예를 들어, B'1'의 값을 입력하면 B'10000000'과 같은 값으로 출력된다. 따라서, 8비트 단위로 크기를 선언하고 8비트 단위로 값을 입력할 것을 권장한다. .. note:: BIT VARYING(4)로 선언된 칼럼에 B'1'을 INSERT하면 CSQL에서는 X'8'로 출력되고, CUBRID Manager 또는 응용 프로그램에서는 X'80'으로 출력된다. * 비트열의 크기가 *n* 을 넘어설 경우에는 절삭되고 0으로 채워진다. * *n* 보다 작은 비트열이 저장될 때에도 나머지 부분이 0으로 채워지지 않는다. * *n* 은 0보다 큰 숫자여야 한다. * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. .. code-block:: sql CREATE TABLE bitvar_tbl(a1 BIT VARYING, a2 BIT VARYING(8)); INSERT INTO bitvar_tbl VALUES (B'1', B'1'); INSERT INTO bitvar_tbl VALUES (0b1010, 0b1010); INSERT INTO bitvar_tbl VALUES (0xaa, 0xaa); INSERT INTO bitvar_tbl(a1) VALUES (0xaaa); INSERT INTO bitvar_tbl(a2) VALUES (0xaaa); SELECT * FROM bitvar_tbl; :: a1 a2 ============================================ X'8' X'8' X'a' X'a' X'aa' X'aa' X'aaa' NULL NULL X'aa' .. _char-data-type: 문자열 데이터 타입 ================== CUBRID는 두 종류의 문자열(character string) 타입을 지원한다. * 고정길이 문자열 : **CHAR** (*n*) * 가변길이 문자열 : **VARCHAR** (*n*) .. note:: **NCHAR**, **NCHAR VARYING** 은 9.0 버전부터 더 이상 지원하지 않으며, 대신 **CHAR**, **VARCHAR** 타입을 사용하도록 한다. 다음은 문자열 타입을 사용할 때 적용되는 규칙이다. * 문자열은 작은 따옴표로 감싸서 표현한다. SQL 구문 관련 파라미터인 **ansi_quotes** 의 값에 따라 문자열을 감싸는 부호로 큰 따옴표도 사용할 수 있다. **ansi_quotes** 값을 no로 설정하면 큰 따옴표로 감싼 문자열을 식별자로 처리하지 않고 문자열로 처리한다. 기본값은 **yes** 이다. 자세한 설명은 :ref:`stmt-type-parameters` 를 참고한다. * ANSI 표준에 따라 두 개의 문자열 사이에 공간으로 취급할 수 있는 문자(예: 공백, 탭, 줄바꿈 등)가 있다면, 두 개의 문자열은 연속된 하나의 문자열로 취급된다. 예를 들면, 다음과 같이 두 개의 문자열 사이에 줄바꿈이 있는 경우가 있다. :: 'abc' 'def' 위 문자열은 아래에 있는 하나의 문자열과 동일하다. :: 'abcdef' * 작은 따옴표 자체를 문자열에 포함시키려면, 두 개의 작은 따옴표를 연속으로 입력하면 된다. 예를 들어, 아래의 왼쪽 문자열은 실제로 오른쪽과 같이 저장된다. :: '''abcde''fghij' 'abcde'fghij * 모든 문자열에 대한 토큰의 최대 크기는 16KB이다. * 특정 국가의 언어를 입력하고자 하는 경우 DB 생성 시 언어의 로캘 이름과 문자셋을 지정하며, 이후 **CHARSET** 소개자(혹은 **COLLATE** 수정자)에 의해 로캘을 변경하여 사용할 수도 있다. 이에 대한 자세한 설명은 :doc:`i18n` 을 참고한다. **길이(Length)** 문자의 개수를 지정한다. 입력된 문자열이 지정된 길이를 초과하는 경우, 지정된 길이에 맞도록 데이터를 자르므로(truncate) 주의한다. 또한, 고정 길이 문자열 타입인 **CHAR** 에서는 선언한 길이에 고정되므로, 문자를 저장할 때 오른쪽에 공백 문자(trailing space)를 채운다. 한편, 가변 길이 문자열 타입인 **VARCHAR** 에서는 공백 문자를 채우지 않고 실제 입력된 문자열만큼 저장한다. **VARCHAR** 타입에서 지정할 수 있는 최대 길이는 1,073,741,823이며, **CHAR** 타입에서 지정할 수 있는 최대 길이는 268,435,455이다. 또한, **CSQL** 문장으로 한 번에 입력 또는 출력할 수 있는 최대 크기는 8192KB이다. .. note:: 9.0 미만 버전에서 **CHAR** 나 **VARCHAR** 타입의 길이는 문자의 개수가 아닌 문자의 바이트 크기를 나타내었다. **문자셋(Character Set, charset)** 문자셋(문자 집합)은 특정 문자(symbol)를 컴퓨터에 저장할 때, 어떠한 코드로 인코딩할 것인지에 대한 규칙이 정의된 집합을 의미한다. CUBRID가 사용할 문자셋은 DB 생성 시, **CHARSET** 소개자 또는 **COLLATE** 수정자 사용 시 지정될 수 있다. 문자셋에 대한 자세한 설명은 :doc:`i18n` 을 참고한다. **문자셋의 정렬(Collating Character Set)** 콜레이션(collation)은 어느 문자셋이 설정된 상태에서 데이터베이스에 저장된 값들을 검색하거나 정렬하는 작업을 위해 문자들을 서로 비교할 때 사용하는 규칙들의 집합이다. 문자셋에 대한 자세한 설명은 :doc:`i18n` 을 참고한다. **문자열 변환(Character String Coercion)** 고정길이와 가변길이 문자열 사이에는 두 문자의 길이가 비교 가능할 수 있도록 자동 변환된다. 자동 변환은 동일한 문자셋에 속하는 문자열에만 적용된다. 예를 들어, 데이터 타입이 **CHAR** (5)인 칼럼을 추출하여 데이터 타입이 **CHAR** (10)인 칼럼에 삽입하는 경우 자동으로 데이터 타입이 **CHAR** (10)으로 변환되어 삽입된다. 문자열을 명시적으로 변환할 수도 있는데, 이 때에는 **CAST** 연산자를 사용한다(:func:`CAST` 참조). .. _string_compression: **문자열 압축(String compression)** 가변 문자열 타입 값(VARCHAR(n))은 데이터베이스(힙 파일, 인덱스 파일 또는 목록 파일)에 저장하기 전에 압축할 수 있다(LZO1X 알고리즘 사용). 최소 255바이트 이상이면 압축이 시도된다(이 값은 미리 정의되어 있으며 변경할 수 없음). 압축이 효율적이지 않으면(압축 값의 크기 및 오버헤드가 압축 전의 원래 값과 동일하거나 큰 경우) 압축되지 않은 채로 값이 저장된다. 압축은 기본적으로 활성화되어 있으며 시스템 파라미터 :ref:`enable_string_compression` 를 설정하여 비활성화할 수 있다. 압축 오버헤드는 8바이트(압축 버퍼 크기 4바이트, 압축 해제 문자열 예상 크기 4바이트)이다. 압축된 문자열은 데이터베이스에서 읽을 때 압축이 풀어진다. 데이터 값의 압축 여부를 파악하려면 :ref:`DISK_SIZE` 함수 결과를 인자가 동일한 :ref:`OCTET_LENGTH` 함수 결과와 비교한다. DISK_SIZE 값이 더 작으면(값 오버헤드 무시) 압축이 사용되었음을 나타낸다. CHAR(n) ------- 고정길이 문자열은 **CHAR** (*n*)로 표현하며, 여기서 *n* 은 문자의 개수를 나타낸다. *n* 이 생략되면 길이는 기본값인 1로 지정된다. 문자열의 길이가 *n* 을 초과하면 **allow_truncated_string** 설정 값이 **yes** 인 경우 초과 부분을 절삭하지만, 설정 값이 **no**\인경우 에러가 발생한다. *n* 보다 작은 문자열이 저장되면 나머지 부분은 공백 문자로 채워진다. **CHAR** (*n*)와 **CHARACTER** (*n*)는 같은 의미로 사용된다. .. note:: CUBRID 9.0 미만 버전에서는 *n* 이 문자의 개수가 아니라 바이트 길이를 나타낸다. * *n* 은 1부터 268,435,455 (256M) 사이의 정수이다. * 공백 값은 빈 따옴표('')로 처리하며, 이 경우 **LENGTH** 함수의 리턴 값은 0이 아니라 **CHAR** (*n*)에서 정의한 고정길이이다. 즉, **CHAR** (10)인 칼럼에 공백 값을 넣더라도 리턴 값은 10이며, *n* 이 생략되면 기본값이 **1** 이므로 **CHAR** (1)로 간주된다. * 채우는(padding) 문자로 사용되는 공백은 특수 문자를 비롯한 어느 문자보다도 작은 것으로 간주된다. :: CHAR(12)에 'pacesetter'를 저장하면 'pacesetter '가 된다(10자리 문자열과 공백 문자 2개로 구성됨). CHAR(10)에 'pacesetter '를 저장하면 'pacesetter'가 된다(10을 넘어서는 부분이 공백 문자이므로 이를 절삭하고 10자리 문자열로 구성됨. 단, **allow_truncated_string** 설정 값이 **no**\인 경우 에러가 발생). CHAR(4)에 'pacesetter'를 저장하면 'pace'가 된다(문자열의 크기가 4보다 크므로 절삭함. 단, **allow_truncated_string** 설정 값이 **no**\인 경우 에러가 발생). CHAR에 'p '를 저장하면 'p'가 된다(n이 생략되면 길이는 기본값인 1로 지정됨). * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. VARCHAR(n) 또는 CHAR VARYING(n) ------------------------------- 가변길이 문자열은 **VARCHAR** (*n*)로 표현하며, 여기서 *n* 은 문자의 개수를 나타낸다. *n* 이 생략되면 길이는 최대 길이인 1,073,741,823로 지정된다. 문자열의 길이가 *n* 을 초과하면 **allow_truncated_string** 설정 값이 **yes**\인 경우 초과 부분을 절삭하지만, 설정 값이 **no**\인경우 에러가 발생한다. *n* 보다 작은 문자열이 저장되면 **CHAR** (*n*)는 나머지 부분을 공백 문자로 채우지만 **VARCHAR** (*n*)에는 해당 문자열 길이만큼만 저장한다. **VARCHAR** (*n*)와 **CHARACTER VARYING** (*n*), **CHAR VARYING** (*n*)은 같은 의미로 사용된다. .. note:: CUBRID 9.0 미만 버전에서는 *n* 이 문자의 개수가 아니라 바이트 길이를 나타낸다. * **STRING** 은 **VARCHAR** (최대 길이)와 같다. * *n* 은 1부터 1,073,741,823(1G) 사이의 정수이다. * 공백 값은 빈 따옴표('')로 처리하며, 이 경우 **LENGTH** 함수의 리턴 값은 0이다. :: VARCHAR(4)에 'pacesetter'를 저장하면 'pace'가 된다(문자열의 크기가 4보다 크므로 절삭함. 단, **allow_truncated_string** 설정 값이 **no**\인 경우 에러가 발생). VARCHAR(12)에 'pacesetter'를 저장하면 'pacesetter'가 된다(10자리 문자열로 구성됨). VARCHAR(12)에 'pacesetter '를 저장하면 'pacesetter '가 된다(10자리 문자열과 공백 문자 2개로 구성됨). VARCHAR(10)에 'pacesetter '를 저장하면 'pacesetter'가 된다(10을 넘어서는 부분이 공백 문자이므로 이를 절삭하고 10자리 문자열로 구성됨. 단, **allow_truncated_string** 설정 값이 **no**\인 경우 에러가 발생). VARCHAR에 'p '를 저장하면 'p '가 된다(n이 생략되면 최대 길이는 기본값인 1,073,741,823로 지정되고, 저장 시 나머지 부분은 공백 문자로 채워지지 않음). * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. STRING ------ **STRING** 은 가변길이 문자열 데이터 타입이다. **STRING** 은 **VARCHAR** 를 최대 길이로 지정한 것과 같다. 즉 **STRING** 은 **VARCHAR** (1,073,741,823)과 동일하다. .. _escape-characters: 특수 문자 이스케이프 -------------------- CUBRID는 특수 문자를 이스케이프(escape)하는 방법을 두 가지 지원한다. 하나는 따옴표를 이용한 방법이고, 다른 하나는 백슬래시(\\)를 이용한 방법이다. * 따옴표 이스케이프 **cubrid.conf** 의 시스템 파라미터 **ansi_quotes**\ 가 no로 설정되어 있으면 문자열을 감쌀 때 큰따옴표(")와 작은따옴표(') 둘 다 사용할 수 있다. **ansi_quotes** 파라미터의 기본값은 **yes** 로, 문자열을 감쌀 때 작은따옴표만 사용할 수 있다. * 작은따옴표로 감싼 문자열에 포함된 작은따옴표는 두 개의 작은따옴표('')를 쓴다. * 큰따옴표로 감싼 문자열에 포함된 큰따옴표는 두 개의 큰따옴표("")를 쓴다. (**ansi_quotes** 값이 no인 경우) * 큰따옴표로 감싼 문자열에 포함된 작은따옴표는 이스케이프하지 않아도 된다. (**ansi_quotes** 값이 no인 경우) * 작은따옴표로 감싼 문자열에 포함된 큰따옴표는 이스케이프하지 않아도 된다. * 백슬래시 이스케이프 백슬래시(\\)를 이용한 이스케이프는 **cubrid.conf**\ 의 시스템 파라미터 **no_backslash_escapes**\ 를 no로 설정했을 때에만 사용할 수 있다. **no_backslash_escapes** 파라미터의 기본값은 **yes** 이다. **no_backslash_escapes**\ 의 값이 no인 경우, 다음과 같은 특수 문자를 의미한다. * \\' : 작은따옴표(') * \\" : 큰따옴표(") * \\n : 뉴라인(newline, linefeed) 문자 * \\r : 캐리지 리턴(carriage return) 문자 * \\t : 탭(tab) 문자 * \\\\ : 백슬래시(backslash) * \\% : 퍼센트 기호(%). 자세한 내용은 아래 설명을 참고한다. * \\_ : 언더바(\_). 자세한 내용은 아래 설명을 참고한다. 다른 모든 이스케이프에 대해서는 백슬래시가 무시된다. 예를 들어 "\x"는 그냥 "x"라고 입력한 것과 같다. **\\%** 와 **\\_** 는 **LIKE** 와 같은 패턴 매칭 구문에서 퍼센트 기호와 언더바를 찾을 때 쓰이며, 백슬래시가 없으면 와일드카드 문자(wildcard character)로 쓰인다. 패턴 매칭 구문 밖에서는 와일드카드 문자가 아닌 일반 문자열 "\\%"와 "\\_"로 그대로 쓰인다. 자세한 내용은 :ref:`like-expr` 을 참고한다. 다음은 **cubrid.conf**\ 의 시스템 파라미터 **ansi_quotes**\ 가 yes(기본값)이고 **no_backslash_escapes**\ 가 no일 때 백슬래시에 대해 이스케이프를 수행한 결과이다. .. code-block:: sql -- ansi_quotes=yes, no_backslash_escapes=no SELECT STRCMP('single quotes test('')', 'single quotes test(\')'); 위의 구문을 실행하면, 백슬래시가 이스케이프 문자로 인식되므로 두 문자열은 같은 것으로 인식된다. :: strcmp('single quotes test('')', 'single quotes test('')') ============================================================= 0 .. code-block:: sql SELECT STRCMP('\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z', 'a\bcdefghijklm\nopq\rs\tuvwxyz'); 위의 구문을 실행하면, 백슬래시가 이스케이프 문자로 인식되므로 두 문자열은 같은 것으로 인식된다. :: strcmp('abcdefghijklm s uvwxyz', 'abcdefghijklm s uvwxyz') ===================================================================== 0 .. code-block:: sql SELECT LENGTH('\\'); 위의 구문을 실행하면, 백슬래시가 이스케이프 문자로 인식되므로 문자열의 길이는 1이 된다. :: char_length('\') =================== 1 다음은 **cubrid.conf**\ 의 시스템 파라미터 **ansi_quotes**\ 가 yes(기본값)이고 **no_backslash_escapes**\ 가 yes(기본값)일 때 수행한 결과이다. 백슬래시는 일반 문자로 인식된다. .. code-block:: sql -- ansi_quotes=yes, no_backslash_escapes=yes SELECT STRCMP('single quotes test('')', 'single quotes test(\')'); 위의 구문을 실행하면, 아직 따옴표가 열린 것으로 간주하여 아래와 같은 에러가 발생한다. CSQL 인터프리터의 SQL 입력 화면에서 위의 구문을 입력하면 다음 따옴표가 입력되기를 대기한다. :: ERROR: syntax error, unexpected UNTERMINATED_STRING, expecting SELECT or VALUE or VALUES or '(' .. code-block:: sql SELECT STRCMP('\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z', 'a\bcdefghijklm\nopq\rs\tuvwxyz'); 위의 구문을 실행하면, 백슬래시가 일반 문자로 인식되므로 두 문자열의 비교 결과는 서로 다른 것으로 인식된다. :: strcmp('\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z', 'a\bcdefghijklm\nopq\rs\tuvwxyz') =================================================================================================== -1 .. code-block:: sql SELECT LENGTH('\\'); 위의 구문을 실행하면, 백슬래시가 일반 문자로 인식되므로 문자열의 길이는 2가 된다. :: char_length('\\') ==================== 2 다음은 **cubrid.conf**\ 의 시스템 파라미터 **ansi_quotes**\ 가 yes이고 **no_backslash_escapes**\ 가 no일 때 LIKE 절에 대해 이스케이프를 수행한 결과이다. .. code-block:: sql -- ansi_quotes=yes, no_backslash_escapes=no CREATE TABLE t1 (a VARCHAR(200)); INSERT INTO t1 VALUES ('aaabbb'), ('aaa%'); SELECT a FROM t1 WHERE a LIKE 'aaa\%' ESCAPE '\\'; :: a ====================== 'aaa%' '%'가 패턴 매칭 문자가 아닌 일반 문자로 인식되기 때문에, 질의를 수행하면 1개의 행만 리턴한다. LIKE 절의 문자열에서는 백슬래시가 항상 일반 문자로 간주되기 때문에, '%'를 패턴 매칭 문자가 아닌 일반 문자로 인식시키려면 ESCAPE 절을 지정하여 백슬래시가 이스케이프 문자임을 명시해야 한다. ESCAPE 절에서는 백슬래시가 이스케이프 문자로 간주되기 때문에, 두 개의 백슬래시 문자를 사용했다. 위의 SELECT 질의에서 이스케이프 문자를 백슬래시가 아닌 다른 문자로 쓰려면 아래와 같이 사용할 수 있다. .. code-block:: sql SELECT a FROM t1 WHERE a LIKE 'aaa#%' ESCAPE '#'; 비교 규칙 --------- 두 문자열 값을 비교할 때 후행 공백에 대한 비교 규칙은 다음과 같다. * 후행 공백 무시 * 후행 공백 포함 **후행 공백 무시** 두 문자열 값이 모두 고정 길이 타입 (CHAR) 인 경우의 비교는 아래 예와 같이 후행 공백을 무시한다. 'abc'와 'abc ' 비교 결과는 "일치" 이다. **후행 공백 포함** 두 문자열 값이 모두 가변 길이 타입 (VARCHAR) 인 경우의 비교는 아래 예와 같이 후행 공백을 무시하지 않는다. 'abc'를 'abc ' 비교 결과는 'abc '가 'abc'보다 "크다" 이다. **예외** 두 문자열 값을 비교할 때 하나는 고정형이고 다른 하나는 변수형이면 CUBRID는 **후행 공백 포함** 규칙을 따른다. ENUM 데이터 타입 ================ **ENUM** 타입은 열거형 문자열 상수들의 중복 없는 순서 집합으로 구성되어 있는 타입이다. 열거형 칼럼을 생성하는 구문은 다음과 같다. :: ::= ENUM '(' ')' ::= ',' CHAR_STRING | CHAR_STRING 다음은 **ENUM** 칼럼을 정의하는 예이다. .. code-block:: sql CREATE TABLE tbl ( color ENUM ('red', 'yellow', 'blue', 'green') ); * 이 타입의 칼럼에 **DEFAULT** 속성이 지정될 수 있다. 색인은 원소들이 열거형 타입에 정의된 순서에 따라 각 원소에 연관되어 있다. 예를 들어, *color* 칼럼(NULL 값을 허용한다고 가정)은 다음 값 중 하나를 가질 수 있다. ========= ============ 값 색인 번호 ========= ============ NULL NULL 'red' 1 'yellow' 2 'blue' 3 'green' 4 ========= ============ **ENUM** 타입 값의 집합은 512 개보다 많은 원소를 가질 수 없으며, 집합의 각 원소는 고유해야 한다. 각 **ENUM** 타입 값에 대해 해당 색인만 저장하기 때문에, **ENUM** 타입은 2 바이트의 저장 공간을 사용한다. 문자열 값이 아니라 해당 색인만 저장함으로써 저장 공간을 줄일 수 있으며 이로 인한 성능 이점도 얻을 수 있다. **ENUM** 타입을 사용할 때 열거형 값과 값의 색인 둘 다 활용할 수 있다. 예를 들어, **ENUM** 타입 칼럼에 값을 삽입할 때 사용자는 **ENUM** 타입의 값 또는 색인을 사용할 수 있다. .. code-block:: sql -- insert enum element 'yellow' with index 2 INSERT INTO tbl (color) VALUES ('yellow'); -- insert enum element 'red' with index 1 INSERT INTO tbl (color) VALUES (1); 표현식에서 사용될 때 **ENUM** 타입은 문맥에 따라 **CHAR** 타입 또는 숫자로 동작한다. .. code-block:: sql -- the first result column has ENUM type, the second has INTEGER type and the third has VARCHAR type SELECT color, color + 0, CONCAT(color, '') FROM tbl; :: color color+0 concat(color, '') ========================================================= 'yellow' 2 'yellow' 'red' 1 'red' 문맥 상 CHAR나 숫자 타입이 아닌 다른 타입으로 사용될 때, 열거형은 색인 또는 열거형 값을 사용하는 해당 타입으로 변환된다. 아래 표는 타입 변환 시 **ENUM** 타입의 어떤 부분이 사용되는지를 보여준다. +---------------+----------------------+ | 타입 | 값(색인/값) | +===============+======================+ | SHORT | 색인 | +---------------+----------------------+ | INTEGER | 색인 | +---------------+----------------------+ | BIGINT | 색인 | +---------------+----------------------+ | FLOAT | 색인 | +---------------+----------------------+ | DOUBLE | 색인 | +---------------+----------------------+ | NUMERIC | 색인 | +---------------+----------------------+ | TIME | 값 | +---------------+----------------------+ | DATE | 값 | +---------------+----------------------+ | DATETIME | 값 | +---------------+----------------------+ | TIMESTAMP | 값 | +---------------+----------------------+ | CHAR | 값 | +---------------+----------------------+ | VARCHAR | 값 | +---------------+----------------------+ | BIT | 값 | +---------------+----------------------+ | VARBIT | 값 | +---------------+----------------------+ ENUM 타입 비교 -------------- **=** 또는 **IN** 연산자가 ( <연산자> <상수>) 형식으로 주어지면, 시스템은 상수를 **ENUM** 타입으로 변환하려고 한다. 변환에 실패하면 오류를 반환하지 않고 비교 결과를 FALSE로 반환하는데, 이와 같은 동작은 이러한 두 개의 연산자에 대한 인덱스 스캔 질의 계획을 허용하기 위해서이다. 다른 모든 :doc:`비교 연산자`\에 대해 **ENUM** 타입은 다른 피연산자의 타입으로 변환된다. 두 개의 **ENUM** 타입 간에 비교 연산이 수행되면 두 피연산자 모두 **CHAR** 타입으로 변환되며 **CHAR** 타입의 규칙을 따른다. **=**\과 **IN** 조건을 제외한 **ENUM** 칼럼을 포함한 나머지 조건은 인덱스 스캔의 고려 대상이 아니다. 이러한 규칙을 이해하기 위해 다음의 예를 살펴보자. .. code-block:: sql CREATE TABLE tbl ( color ENUM ('red', 'yellow', 'blue', 'green') ); INSERT INTO tbl (color) VALUES (1), (2), (3), (4); 다음 질의는 상수 'red'를 색인 번호 1을 가진 열거형 값 'red'로 변환한다. .. code-block:: sql SELECT color FROM tbl WHERE color = 'red'; :: color ====================== 'red' .. code-block:: sql SELECT color FROM tbl WHERE color = 1; :: color ====================== 'red' 다음 질의는 변환 오류를 반환하지는 않지만 어떠한 결과도 반환하지 않는다. .. code-block:: sql SELECT color FROM tbl WHERE color = date'2010-01-01'; SELECT color FROM tbl WHERE color = 15; SELECT color FROM tbl WHERE color = 'asdf'; 다음 질의에서 **ENUM** 타입은 다른 피연산자의 타입으로 변환된다. .. code-block:: sql -- CHAR comparison using the enum value SELECT color FROM tbl WHERE color < 'pink'; :: color ====================== 'blue' 'green' .. code-block:: sql -- INTEGER comparison using the enum index SELECT color FROM tbl WHERE color > 3; :: color ====================== 'green' .. code-block:: sql -- Conversion error SELECT color FROM tbl WHERE color > date'2012-01-01'; :: ERROR: Cannot coerce value of domain "enum" to domain "date". ENUM 타입 정렬 -------------- **ENUM** 타입 값은 열거형 값이 아니라 값의 색인에 의해 정렬된다. **ENUM** 타입 칼럼을 정의할 때는 열거되는 값의 순서도 함께 고려해야 한다. .. code-block:: sql SELECT color FROM tbl ORDER BY color ASC; :: color ====================== 'red' 'yellow' 'blue' 'green' **ENUM** 타입 칼럼으로 저장된 값을 **CHAR** 값으로 정렬하려면 열거형 값을 **CHAR** 타입으로 변환(CAST)하면 된다. .. code-block:: sql SELECT color FROM tbl ORDER BY CAST (color AS VARCHAR) ASC; :: color ====================== 'blue' 'green' 'red' 'yellow' 참고 사항 --------- **ENUM** 타입은 재사용 가능한 타입이 아니다. 여러 개의 칼럼이 같은 ENUM 집합의 값을 요구한다 하더라도, 각각의 칼럼에 대해 **ENUM** 타입이 정의되어야 한다. **ENUM** 타입의 두 칼럼을 비교하는 경우 비록 두 **ENUM** 타입이 같은 집합 값을 정의했다고 하더라도, 두 칼럼이 **CHAR** 타입으로 변환된 것처럼 동작한다. **ALTER ... CHANGE** 문을 사용하여 **ENUM** 타입 값의 집합을 수정하려면 **alter_table_change_type_strict** 파라미터의 값이 반드시 yes여야 한다. 이 경우 시스템은 새로운 타입으로 변환한 열거형 값(문자열 리터럴)을 사용한다. 기존 값이 새로운 **ENUM** 타입 값의 집합을 벗어나면 자동으로 공백 문자열('')로 매핑된다. .. code-block:: sql CREATE TABLE tbl(color ENUM ('red', 'green', 'blue')); INSERT INTO tbl VALUES('red'), ('green'), ('blue'); 다음 문장은 **ENUM** 타입이 'yellow' 값을 가지도록 변경한다. .. code-block:: sql ALTER TABLE tbl CHANGE color color ENUM ('red', 'green', 'blue', 'yellow'); INSERT into tbl VALUES(4); SELECT color FROM tbl; :: color ====================== 'red' 'green' 'blue' 'yellow' 다음의 예에서 'green' 값이 새로운 **ENUM** 타입 정의에 매핑되지 않기 때문에, 모든 투플의 'green'이 ''로 변경된다. .. code-block:: sql ALTER TABLE tbl CHANGE color color ENUM ('red', 'yellow', 'blue'); SELECT color FROM tbl; :: color ====================== 'red' '' 'blue' 'yellow' **ENUM** 타입은 CUBRID 드라이버에서 문자열로 매핑된다. 다음은 JDBC 응용에서 **ENUM** 타입을 사용하는 예이다. .. code-block:: java Statement stmt = connection.createStatement("SELECT color FROM tbl"); ResultSet rs = stmt.executeQuery(); while(rs.next()){ System.out.println(rs.getString()); } 다음은 CCI 응용에서 **ENUM** 타입을 사용하는 예이다. .. code-block:: c req_id = cci_prepare (conn, "SELECT color FROM tbl", 0, &err); error = cci_execute (req_id, 0, 0, &err); if (error < CCI_ER_NO_ERROR) { /* handle error */ } error = cci_cursor (req_id, 1, CCI_CURSOR_CURRENT, &err); if (error < CCI_ER_NO_ERROR) { /* handle error */ } error = cci_fetch (req_id, &err); if (error < CCI_ER_NO_ERROR) { /* handle error */ } cci_get_data (req, idx, CCI_A_TYPE_STR, &data, 1); .. _blob-clob: BLOB/CLOB 데이터 타입 ===================== External LOB(Large Object) 타입은 텍스트 또는 이미지 등 크기가 큰 객체를 처리하기 위한 데이터 타입이다. **LOB** 타입 데이터가 생성 및 삽입되면 이는 외부 저장소에 파일로 저장되고 CUBRID 데이터베이스에는 해당 파일의 위치 정보(**LOB** locator)가 저장된다. 데이터베이스에서 해당 데이터(**LOB** locator)가 삭제되면, 외부 저장소에 저장된 해당 파일이 함께 삭제된다. CUBRID는 두 가지 **LOB** 타입을 지원한다. * Binary Large Object(**BLOB**) * Character Large Object(**CLOB**) .. note:: 관련 용어 * **LOB** (Large Object): 이진 바이너리 또는 텍스트 등 크기가 큰 객체이다. * **FBO** (File Based Object): DB 데이터를 DB 외부의 파일로 저장하는 객체이다. * **External LOB**\ : LOB 데이터를 DB 외부에 파일로 저장하는 객체로서 FBO라고도 하며, CUBRID는 이를 지원한다. Internal LOB은 **LOB** 데이터를 DB 내부에 저장하는 객체이다. * **External Storage**\ : LOB을 저장하는 외부 저장소이다(예: POSIX 파일 시스템). * **LOB Locator**\ : 외부 저장소에 저장된 파일의 경로명이다. * **LOB Data**\ : LOB Locator에 명시된 위치에 있는 파일의 내용이다. **LOB** 데이터는 외부 저장소에 다음과 같은 파일명으로 저장된다. :: {table_name}_{unique_name} * *table_name* : prefix로 삽입되어 하나의 외부 저장소에 여러 테이블의 **LOB** 데이터를 저장할 수 있다. * *unique_name* : 데이터베이스 서버가 임의로 생성하는 이름이다. **LOB** 데이터의 저장소는 데이터베이스 서버 상의 로컬 파일 시스템이다. **LOB** 데이터는 **cubrid createdb** 유틸리티의 **-lob-base-path** 옵션 값으로 지정된 경로에 저장되며, 옵션이 생략될 경우 데이터베이스 볼륨이 생성되는 [db-vol path]/lob 경로에 저장된다. 보다 자세한 내용은 :ref:`creating-database` 및 :ref:`lob-storage` 를 참고한다. CUBRID가 제공하는 API나 도구를 사용하지 않고 사용자가 임의로 **LOB** 파일 내용을 수정하면, 해당 내용의 일치성이 보장되지 않으므로 주의한다. 데이터베이스 위치 정보 파일(**databases.txt**)에 **LOB** 데이터 파일 경로가 등록되어 있음에도 불구하고 해당 경로가 삭제된 경우, 데이터베이스 서버(**cub_server**) 및 독립 모드(standalone)로 동작하는 유틸리티가 정상적으로 실행되지 않으므로 주의한다. BLOB ---- 바이너리 데이터를 DB 외부에 저장하기 위한 타입으로, **BLOB** 데이터의 최대 길이는 외부 저장소에서 생성 가능한 파일 크기이다. **BLOB** 타입은 SQL 문에서 비트열 타입으로 입출력 값을 표현한다. 즉, **BIT** (n), **BIT VARYING** (n) 타입과 호환되며, 명시적 타입 변환만 허용된다. 데이터 길이가 서로 다른 경우에는 최대 길이가 작은 타입에 맞추어 절삭(truncate)된다. **BLOB** 타입 값을 바이너리 값으로 변환하는 경우, 변환된 데이터는 최대 1GB를 넘을 수 없다. 반대로 바이너리를 **BLOB** 타입으로 변환하는 경우, 변환된 데이터는 **BLOB** 저장소에서 제공하는 최대 파일 크기를 넘을 수 없다. CLOB ---- 문자열 데이터를 DB 외부에 저장하기 위한 타입으로, **CLOB** 데이터의 최대 길이는 외부 저장소에서 생성 가능한 파일 크기이다. **CLOB** 타입은 SQL 문에서 문자열 타입으로 입출력 값을 표현한다. 즉, **CHAR** (n), **VARCHAR** (n) 타입과 호환된다. 단, 명시적 타입 변환만 허용되며, 데이터 길이가 서로 다른 경우에는 최대 길이가 작은 타입에 맞추어 절삭(truncate)된다. **CLOB** 타입 값을 문자열 값으로 변환하는 경우, 변환된 데이터는 최대 1GB를 넘을 수 없다. 반대로 문자열을 **CLOB** 타입으로 변환하는 경우, 변환된 데이터는 **CLOB** 저장소에서 제공하는 최대 파일 크기를 넘을 수 없다. 정의 및 변경 ------------ **CREATE TABLE** 문 또는 **ALTER TABLE** 문을 사용하여 **BLOB** / **CLOB** 타입 칼럼을 생성/추가/삭제할 수 있다. * **LOB** 타입 칼럼에 대해서는 인덱스를 생성할 수 없다. * **LOB** 타입 칼럼에 대해서는 **PRIMARY KEY**, **FOREIGN KEY**, **UNIQUE**, **NOT NULL** 제약 조건을 정의할 수 없다. 또한, **SHARED** 속성을 정의할 수 없으며, **DEFAULT** 속성은 **NULL** 값에 대해서만 정의할 수 있다. * **LOB** 타입 칼럼/데이터는 컬렉션 타입의 원소가 될 수 없다. * **LOB** 타입 칼럼이 있는 레코드를 삭제하는 경우, **LOB** 칼럼 값(Locator) 및 외부 저장소 내 파일을 모두 삭제한다. 또한, 기본 키 테이블에서 **LOB** 타입 칼럼이 있는 레코드가 삭제됨에 따라 이를 참조하는 외래 키 테이블의 레코드가 함께 삭제되는 경우, **LOB** 칼럼 값(Locator) 및 외부 저장소 내 **LOB** 파일을 모두 삭제한다. 단, **ALTER TABLE ... DROP** 문을 사용하여 **LOB** 칼럼을 삭제하거나 **DROP TABLE** 문을 사용하여 해당 테이블을 삭제하는 경우, **LOB** 칼럼 값(Lob locator)만 삭제하고 **LOB** 칼럼이 참조하는 외부 저장소 내 **LOB** 파일은 삭제하지 않는다. .. code-block:: sql -- creating a table and CLOB column CREATE TABLE doc_t (doc_id VARCHAR(64) PRIMARY KEY, content CLOB); -- an error occurs when UNIQUE constraint is defined on CLOB column ALTER TABLE doc_t ADD CONSTRAINT content_unique UNIQUE(content); -- an error occurs when creating an index on CLOB column CREATE INDEX i_doc_t_content ON doc_t (content); -- creating a table and BLOB column CREATE TABLE image_t (image_id VARCHAR(36) PRIMARY KEY, doc_id VARCHAR(64) NOT NULL, image BLOB); -- an error occurs when adding a BOLB column with NOT NULL constraint ALTER TABLE image_t ADD COLUMN thumbnail BLOB NOT NULL; -- an error occurs when adding a BLOB column with DEFAULT attribute ALTER TABLE image_t ADD COLUMN thumbnail2 BLOB DEFAULT BIT_TO_BLOB(X'010101'); 저장 및 변경 ------------ **BLOB** / **CLOB** 타입 칼럼에는 각각 **BLOB** / **CLOB** 타입 값이 저장되며, 바이너리 또는 문자열 데이터를 입력하는 경우에는 각각 :func:`BIT_TO_BLOB`, :func:`CHAR_TO_CLOB` 함수를 사용하여 명시적으로 타입을 변환을 수행하여야 한다. **INSERT** 문을 사용하여 **LOB** 칼럼에 값을 입력하면, 내부적으로는 외부 저장소에 파일을 생성하여 해당 데이터를 저장하고, 실제 칼럼 값으로 해당 파일의 경로(Locator) 정보를 저장한다. **DELETE** 문을 사용하여 **LOB** 칼럼이 존재하는 레코드를 삭제하면, 해당 **LOB** 칼럼 값이 참조하는 파일을 함께 삭제한다. **UPDATE** 문을 사용하여 **LOB** 칼럼 값을 변경하는 경우, 새로운 값이 **NULL** 인지에 따라 다음과 같이 동작하면서 칼럼 값을 변경한다. * **LOB** 타입 칼럼 값을 **NULL** 이 아닌 값으로 변경하는 경우: **LOB** 칼럼에 이미 외부 파일을 참조하는 Locator 가 저장되어 있다면, 해당 파일을 삭제한다. 그리고 새로운 파일을 생성하여 NULL이 아닌 값을 저장한 후, **LOB** 칼럼 값에 새로운 파일에 대한 locator를 저장한다. * **LOB** 타입 칼럼 값을 **NULL** 로 변경하는 경우: LOB 칼럼에 이미 외부 파일을 참조하는 Locator 가 저장되어 있다면, 해당 파일을 삭제한다. 그리고 **LOB** 칼럼 값에 **NULL**\ 을 바로 저장한다. .. code-block:: sql -- inserting data after explicit type conversion into CLOB type column INSERT INTO doc_t (doc_id, content) VALUES ('doc-1', CHAR_TO_CLOB('This is a Dog')); INSERT INTO doc_t (doc_id, content) VALUES ('doc-2', CHAR_TO_CLOB('This is a Cat')); -- inserting data after explicit type conversion into BLOB type column INSERT INTO image_t VALUES ('image-0', 'doc-0', BIT_TO_BLOB(X'000001')); INSERT INTO image_t VALUES ('image-1', 'doc-1', BIT_TO_BLOB(X'000010')); INSERT INTO image_t VALUES ('image-2', 'doc-2', BIT_TO_BLOB(X'000100')); -- inserting data from a sub-query result INSERT INTO image_t SELECT 'image-1010', 'doc-1010', image FROM image_t WHERE image_id = 'image-0'; -- updating CLOB column value to NULL UPDATE doc_t SET content = NULL WHERE doc_id = 'doc-1'; -- updating CLOB column value UPDATE doc_t SET content = CHAR_TO_CLOB('This is a Dog') WHERE doc_id = 'doc-1'; -- updating BLOB column value UPDATE image_t SET image = (SELECT image FROM image_t WHERE image_id = 'image-0') WHERE image_id = 'image-1'; -- deleting BLOB column value and its referencing files DELETE FROM image_t WHERE image_id = 'image-1010'; 조회 ---- **LOB** 타입 칼럼을 조회하면 칼럼이 참조하는 파일에 저장된 데이터를 출력한다. :func:`CAST` 연산자, :func:`CLOB_TO_CHAR` 함수, :func:`BLOB_TO_BIT` 함수를 사용하여 명시적 타입 변환을 수행할 수 있다. * CSQL에서 질의를 실행할 경우, 파일에 저장된 데이터가 아닌 칼럼 값(Locator)을 출력한다. **BLOB** / **CLOB** 칼럼이 참조하는 데이터를 출력하기 위해서는 :func:`CLOB_TO_CHAR` 함수를 사용하여 문자열로 변환해야 한다. * 문자열 처리 함수를 사용하기 위해서는 :func:`CLOB_TO_CHAR` 함수를 사용하여 문자열로 변환해야 한다. * **GROUP BY** 절, **ORDER BY** 절에 **LOB** 칼럼을 명시할 수 없다. * 비교 연산자, 관계 연산자, **IN**, **NOT IN** 연산자를 사용하여 **LOB** 칼럼을 비교할 수 없다. 단, **IS NULL** 조건식을 사용하여 **LOB** 칼럼 값(Locator)이 **NULL** 인지 비교할 수 있다. 즉, 칼럼 값이 **NULL**\ 이면 **TRUE**\ 를 반환하는데, 칼럼 값이 **NULL**\ 인 경우는 **LOB** 데이터를 저장하는 파일이 존재하지 않는다는 의미이다. * **LOB** 칼럼을 생성하고 데이터를 입력한 이후 **LOB** 데이터 파일을 삭제하면, **LOB** 칼럼 값(Locator)이 유효하지 않은 파일을 참조하는 상태가 된다. 이처럼 **LOB** locator와 **LOB** 데이터 파일이 매칭되지 않는 칼럼에 대해 :func:`CLOB_TO_CHAR`, :func:`BLOB_TO_BIT`, :func:`CLOB_LENGTH`, :func:`BLOB_LENGTH` 함수를 사용하면 **NULL**\ 을 출력한다. .. code-block:: sql -- displaying locator value when selecting CLOB and BLOB column in CSQL interpreter SELECT doc_t.doc_id, content, image FROM doc_t, image_t WHERE doc_t.doc_id = image_t.doc_id; :: doc_id content image ================================================================== 'doc-1' file:/home1/data1/ces_658/doc_t.00001282208855807171_7329 file:/home1/data1/ces_318/image_t.00001282208855809474_7474 'doc-2' file:/home1/data1/ces_180/doc_t.00001282208854194135_5598 file:/home1/data1/ces_519/image_t.00001282208854205773_1215 2 rows selected. .. code-block:: sql -- using string functions after coercing its type by CLOB_TO_CHAR( ) SELECT CLOB_TO_CHAR(content), SUBSTRING(CLOB_TO_CHAR(content), 10) FROM doc_t; :: clob_to_char(content) substring( clob_to_char(content) from 10) ============================================ 'This is a Dog' ' Dog' 'This is a Cat' ' Cat' 2 rows selected. .. code-block:: sql SELECT CLOB_TO_CHAR(content) FROM doc_t WHERE CLOB_TO_CHAR(content) LIKE '%Dog%'; :: clob_to_char(content) ====================== 'This is a Dog' .. code-block:: sql SELECT CLOB_TO_CHAR(content) FROM doc_t ORDER BY CLOB_TO_CHAR(content); :: clob_to_char(content) ====================== 'This is a Cat' 'This is a Dog' .. code-block:: sql SELECT * FROM doc_t WHERE content LIKE 'This%'; :: doc_id content ============================================ 'doc-1' file:/home1/data1/ces_004/doc_t.00001366272829040346_0773 'doc-2' file:/home1/data1/ces_256/doc_t.00001366272815153996_1229 .. code-block:: sql -- an error occurs when LOB column specified in ORDER BY/GROUP BY clauses SELECT * FROM doc_t ORDER BY content; :: ERROR: doc_t.content can not be an ORDER BY column 연산자와 함수 ------------- :func:`CAST` 연산자를 사용하여 **BLOB** / **CLOB** 타입과 바이너리 타입/문자열 타입 간 명시적 타입 변환을 수행할 수 있다. 자세한 내용은 :func:`CAST` 연산자를 참고한다. :: CAST ( AS { BLOB | CLOB }) CAST ( AS { BLOB | CLOB }) 다음은 **BLOB** / **CLOB** 타입 처리 및 변환을 위해 제공하는 함수이다. 자세한 설명은 :doc:`/sql/function/lob_fn` 절을 참고한다. * :func:`CLOB_TO_CHAR` * :func:`BLOB_TO_BIT` * :func:`CHAR_TO_CLOB` * :func:`BIT_TO_BLOB` * :func:`CHAR_TO_BLOB` * :func:`CLOB_FROM_FILE` * :func:`BLOB_FROM_FILE` * :func:`CLOB_LENGTH` * :func:`BLOB_LENGTH` .. note:: " <*blob_or_clob_column*> **IS NULL** ": **IS NULL** 조건식을 사용하여 **LOB** 칼럼 값(Locator)이 **NULL** 인지 비교하고, **NULL** 이면 **TRUE** 를 반환한다. .. _lob-storage: 저장소 생성 및 관리 ------------------- **LOB** 데이터 파일은 기본적으로 데이터베이스 볼륨이 생성되는 /lob 디렉터리에 저장된다. 데이터베이스 생성 시 :option:`createdb -B` 옵션을 통해서 **LOB** 데이터 파일을 저장할 디렉터리를 지정할 수 있다. 지정된 디렉터리가 존재하지 않으면 디렉터리 생성을 시도하며, 생성 실패 시에는 에러를 출력한다. 자세한 내용은 :option:`createdb -B` 옵션을 참고한다. :: # 현재 작업 디렉터리에 image_db 볼륨이 생성되고 LOB 데이터 파일이 저장된다. % cubrid createdb image_db en_US # 로컬 파일 시스템 내 "/home1/data1" 경로에 LOB 데이터 파일이 저장된다. % cubrid createdb --lob-base-path="file:/home1/data1" image_db en_US :program:`spacedb` 유틸리티를 실행하여 LOB 파일이 저장되는 디렉터리를 확인할 수 있다. :: % cubrid spacedb image_db Space description for database 'image_db' with pagesize 16.0K. (log pagesize: 16.0K) Volid Purpose total_size free_size Vol Name 0 GENERIC 512.0M 510.1M /home1/data1/image_db Space description for temporary volumes for database 'image_db' with pagesize 16.0K. Volid Purpose total_size free_size Vol Name LOB space description file:/home1/data1 파일 저장소를 추가로 생성하려면 디스크 공간을 확보한 후 **databases.txt** 의 **lob-base-path** 를 증설한 디스크 위치로 변경한다. **databases.txt** 의 변경 내용을 반영하기 위하여 DB 서버를 재구동한다. 단, **databases.txt** 의 **lob-base-path** 를 변경하더라도 예전 저장소에 저장된 **LOB** 데이터는 접근 가능하다. :: # You can change to a new directory from the lob-base-path of databases.txt file. % cat $CUBRID_DATABASES/databases.txt #db-name vol-path db-host log-path lob-base-path image_db /home1/data1 localhost /home1/data1 file:/home1/data2 **LOB** 타입 칼럼의 데이터 파일에 대한 백업 및 복구는 지원하지 않으며, **LOB** 타입 칼럼의 메타 데이터 값(Locator)에 대해서만 백업 및 복구를 지원한다. :program:`copydb` 유틸리티를 사용하여 데이터베이스를 복사하는 경우, 관련 옵션이 지정되지 않으면 **LOB** 파일 디렉터리 경로가 복사되지 않으므로 추가로 **databases.txt** 파일을 설정해야 한다. 자세한 내용은 :program:`copydb` 유틸리티의 :option:`copydb -B` 및 :option:`copydb --copy-lob-path` 옵션을 참고한다. 트랜잭션 지원 및 복구 --------------------- **LOB** 데이터 변경에 대한 커밋/롤백을 지원한다. 즉, 트랜잭션 내에서 **LOB** Locator와 실제 **LOB** 데이터의 매핑(mapping)에 대한 유효성을 보장하고, 데이터베이스 장애 시 회복(recovery)을 지원한다. 트랜잭션 수행 도중 데이터베이스가 종료되어 해당 트랜잭션이 롤백 처리됨에 따라 **LOB** Locator와 **LOB** 데이터 간 매핑이 일치하지 않으면 에러를 출력한다. 아래의 예를 참고한다. .. code-block:: sql -- csql> ;AUTOCOMMIT OFF CREATE TABLE doc_t (doc_id VARCHAR(64) PRIMARY KEY, content CLOB); INSERT INTO doc_t VALUES ('doc-10', CHAR_TO_CLOB('This is content')); COMMIT; UPDATE doc_t SET content = CHAR_TO_CLOB('This is content 2') WHERE doc_id = 'doc-10'; ROLLBACK; SELECT doc_id, CLOB_TO_CHAR(content) FROM doc_t WHERE doc_id = 'doc-10'; :: doc_id content ========================================================= 'doc-10' 'This is content' .. code-block:: sql -- csql> ;AUTOCOMMIT OFF INSERT INTO doc_t VALUES ('doc-11', CHAR_TO_CLOB ('This is content')); COMMIT; UPDATE doc_t SET content = CHAR_TO_CLOB('This is content 3') WHERE doc_id = 'doc-11'; -- system crash occurred and then restart server SELECT doc_id, CLOB_TO_CHAR(content) FROM doc_t WHERE doc_id = 'doc-11'; :: -- Error : LOB Locator references to the previous LOB data because only LOB Locator is rollbacked. .. note:: * JDBC와 같은 드라이버를 통해 애플리케이션에서 **LOB** 데이터를 조회하는 경우, JDBC는 데이터베이스 서버로부터 ResultSet을 가져온 후 Resultset에 대해 커서(cursor) 위치를 변경하면서 레코드를 인출할 수 있다. 즉, ResultSet을 가져온 시점에는 **LOB** 칼럼의 메타 데이터인 Locator만 저장되어 있고, 레코드를 인출하는 시점에 Locator가 참조하는 파일로부터 **LOB** 데이터가 실제로 인출된다. 따라서, 두 시점 사이에 **LOB** 데이터가 업데이트되는 경우, **LOB** Locator와 실제 **LOB** 데이터의 매핑(mapping)이 유효하지 않아 에러가 발생할 수 있다. * **LOB** 타입 칼럼의 메타 데이터(Locator)에 대해서만 백업/복구를 지원한다. 따라서, 장애가 발생해서 특정 시점으로 복구할 때 **LOB** Locator와 **LOB** 데이터의 매핑이 유효하지 않아 에러가 발생할 수 있다. * **LOB** 데이터를 다른 장비에 **INSERT** 하려면 반드시 **LOB** 칼럼 메타 데이터(Locator)가 참조하는 **LOB** 데이터를 읽어서 **INSERT** 해야 한다. * CUBRID HA에서 **LOB** 칼럼 메타 데이터(Locator)는 복제되고, **LOB** 데이터는 복제되지 않는다. 따라서 **LOB** 타입 저장소가 로컬에 위치할 경우, 슬레이브 노드 또는 failover 이후 마스터 노드에서 해당 칼럼에 대한 작업을 허용하지 않는다. .. warning:: CUBRID 2008 R3.0 이하 버전에서는 **glo** (Generalized Large Object) 클래스를 사용하여 Large Object를 처리했으나, CUBRID 2008 R3.1 버전부터는 **glo** 클래스를 제거하고 **BLOB** / **CLOB** 데이터 타입을 지원한다. 따라서, 2008 R3.0 미만 버전의 **glo** 클래스를 사용하는 환경에서는 CUBRID 버전 업그레이드를 수행할 때 DB 스키마 및 애플리케이션을 수정해야 한다. .. _collection-data-type: 컬렉션 데이터 타입 ================== 여러 개의 데이터 값을 하나의 속성에 저장할 수 있도록 하는 컬렉션 데이터 타입은 관계형 데이터베이스의 확장된 기능이다. 컬렉션의 각 원소는 하나의 데이터 타입이어야 하며, 테이블(단, 뷰는 제외) 타입이 될 수도 있다. BLOB, CLOB 타입을 제외한 나머지 타입들은 컬렉션 타입의 원소가 될 수 있다. +---------------+---------------------------------------+------------------------------------+----------------------------+----------------------------+ | 타입 | 설명 | 타입 정의 | 입력 데이터 | 저장 데이터 | +===============+=======================================+====================================+============================+============================+ | **SET** | 중복을 허용하지 않는 합집합 | col_name SET VARCHAR(20) 또는 | {'c','c','c','b','b','a'} | {'a','b','c'} | | | | col_name SET (VARCHAR(20)) | | | +---------------+---------------------------------------+------------------------------------+----------------------------+----------------------------+ | **MULTISET** | 중복을 허용하는 합집합 | col_name MULTISET VARCHAR(20) 또는 | {'c','c','c','b','b','a'} | {'a','b','b','c','c','c'} | | | | col_name MULTISET (VARCHAR(20)) | | | +---------------+---------------------------------------+------------------------------------+----------------------------+----------------------------+ | **LIST** 또는 | 중복을 허용하고, | col_name LIST VARCHAR(20) 또는 | {'c','c','c','b','b','a'} | {'c','c','c','b','b','a'} | | **SEQUENCE** | 데이터 입력 순서대로 저장하는 합집합 | col_name LIST (VARCHAR(20)) | | | +---------------+---------------------------------------+------------------------------------+----------------------------+----------------------------+ 위의 표와 같이, 컬렉션 타입으로 지정되는 값은 중괄호('{', '}') 안에 각 값들을 쉼표(,)로 구분하여 나열할 수 있다. 컬렉션 타입은 지정된 타입이 같다면 **CAST** 연산자를 이용하여 명시적으로 타입 변환이 가능하다. 아래 표는 명시적 변환이 가능한 컬렉션 타입에 관한 것이다. +--------------+-----+----------+------+ | FROM \\ TO | SET | MULTISET | LIST | +==============+=====+==========+======+ | **SET** | \- | Yes | Yes | +--------------+-----+----------+------+ | **MULTISET** | Yes | \- | No | +--------------+-----+----------+------+ | **LIST** | Yes | Yes | \- | +--------------+-----+----------+------+ 컬렉션 타입은 콜레이션을 지원하지 않는다. 따라서 다음과 같은 질의는 오류를 반환한다. .. code-block:: sql CREATE TABLE tbl (str SET (string) COLLATE utf8_en_ci); :: Syntax error: unexpected 'COLLATE', expecting ',' or ')' SET --- **SET** 는 각 원소가 서로 다른 값을 갖는 집합이다. **SET** 의 원소는 하나의 데이터 타입을 가져야 하고, 다른 테이블의 레코드를 가질 수도 있다. .. code-block:: sql CREATE TABLE set_tbl (col_1 SET (CHAR(1))); INSERT INTO set_tbl VALUES ({'c','c','c','b','b','a'}); INSERT INTO set_tbl VALUES ({NULL}); INSERT INTO set_tbl VALUES ({''}); SELECT * FROM set_tbl; :: col_1 ====================== {'a', 'b', 'c'} {NULL} {' '} .. code-block:: sql SELECT CAST (col_1 AS MULTISET), CAST (col_1 AS LIST) FROM set_tbl; :: cast(col_1 as multiset) cast(col_1 as sequence) ============================================ {'a', 'b', 'c'} {'a', 'b', 'c'} {NULL} {NULL} {' '} {' '} .. code-block:: sql INSERT INTO set_tbl VALUES (''); :: ERROR: Casting '' to type set is not supported. MULTISET -------- **MULTISET** 는 중복이 허용되는 집합이다. **MULTISET** 의 원소는 하나의 데이터 타입을 가져야 하고, 다른 테이블의 레코드를 가질 수도 있다. .. code-block:: sql CREATE TABLE multiset_tbl (col_1 MULTISET (CHAR(1))); INSERT INTO multiset_tbl VALUES ({'c','c','c','b','b', 'a'}); SELECT * FROM multiset_tbl; :: col_1 ====================== {'a', 'b', 'b', 'c', 'c', 'c'} .. code-block:: sql SELECT CAST(col_1 AS SET), CAST(col_1 AS LIST) FROM multiset_tbl; :: cast(col_1 as set) cast(col_1 as sequence) ============================================ {'a', 'b', 'c'} {'c', 'c', 'c', 'b', 'b', 'a'} LIST 또는 SEQUENCE ------------------ **LIST** (**SEQUENCE**) 는 원소가 입력된 순서가 유지되며, 중복이 허용되는 집합이다. **LIST** 의 원소는 하나의 데이터 타입을 가져야 하고, 다른 테이블의 레코드를 가질 수도 있다. .. code-block:: sql CREATE TABLE list_tbl (col_1 LIST (CHAR(1))); INSERT INTO list_tbl VALUES ({'c','c','c','b','b', 'a'}); SELECT * FROM list_tbl; :: col_1 ====================== {'c', 'c', 'c', 'b', 'b', 'a'} .. code-block:: sql SELECT CAST(col_1 AS SET), CAST(col_1 AS MULTISET) FROM list_tbl; :: cast(col_1 as set) cast(col_1 as multiset) ============================================ {'a', 'b', 'c'} {'a', 'b', 'b', 'c', 'c', 'c'} .. _json-data-type: JSON 데이터 타입 ================ CUBRID 10.2는 `RFC 7159 `__ 에서 정의된 native **JSON** 데이터 타입을 지원한다. **JSON** 데이터 타입은 JSON 데이터에 대해 자동 검증을 제공하며, 빠른 액세스와 동작이 가능하다. .. note:: 10.2 서버에 연결하는 그 이전 버전의 드라이버는 JSON 타입 컬럼을 Varchar 타입으로 인식한다. JSON 데이터 생성 ---------------- JSON 포맷의 문자열 형식이 JSON 데이터 타입 컬럼에 입력되면 JSON 값으로 자동으로 변환된다. .. code-block:: sql -- 문자열을 JSON 타입 컬럼에 입력 CREATE TABLE t (id int, j JSON); INSERT INTO t VALUES (1, '{"a":1}'); SELECT j, TYPEOF(j) FROM t; :: j typeof(j) ============================================ {"a":1} 'json' :ref:`castfn` 를 사용하거나 문자열 앞에 json 키워드를 사용하여 강제로 JSON으로 변환 할 수도 있다. .. code-block:: sql -- 문자열을 json으로 cast SELECT CAST('{"a":1}' as JSON); :: cast('{"a":1}' as json) ====================== {"a":1} .. code-block:: sql -- json 키워드 사용 SELECT json'{"a":1}', TYPEOF (json'{"a":1}'); :: json '{"a":1}' typeof(json '{"a":1}') ============================================ {"a":1} 'json' JSON 데이터 타입은 :ref:`fn-json-object` 나 :ref:`fn-json-array` 를 사용하여 생성할 수도 있다. JSON 유효성 검사 ---------------- JSON 데이터로의 변환은 내장된 유효성 검사를 수행하고 유효한 JSON 문자열이 아닌 경우 오류를 반환한다. .. code-block:: sql -- 따옴표가 없는 문자열은 유효한 json이 아니다 SELECT json'abc'; :: In line 1, column 8, 에러: before ' ; ' 유효하지 않은 JSON: 'abc'. 더 엄격한 유효성 검사 규칙을 가진 JSON 타입 컬럼은 `JSON 스키마 표준 초안 (draft JSON Schema standard) `_ 를 사용하여 정의할 수 있다. 만약 JSON 스키마를 다루어본 적이 없다면 `JSON 스키마의 이해 (Understanding JSON Schema) `_ 를 참고하면 된다. 다음은 스키마 사용 방법에 대한 간단한 예제이다.: .. code-block:: sql -- j 컬럼이 JSON의 문자열 타입만을 입력할 수 있도록 설정 CREATE TABLE t (id int, j JSON ('{"type": "string"}')); .. code-block:: sql -- JSON 스키마에 유효한 문자열 타입 JSON 입력 INSERT into t values (1, '"abc"'); :: 1 개의 명령어가 실행되었습니다. .. code-block:: sql -- JSON 스키마에 유효하지 않은 문자열 타입 JSON 입력 INSERT into t values (2, '{"a":1}'); :: 에러: before ' ); ' 주어진 JSON 은 JSON schema에 대해 유효하지 않습니다. (스키마 경로: #, 키워드: type, JSON 경로: #) JSON 데이터의 타입 -------------------- JSON 데이터의 값은 `RFC 7159 ` 에서 정의된 것과 같이 객체 (Object), 배열 (Array) 또는 스칼라 (Scalar) 여야 한다. 스칼라 값은 문자열, 숫자형, 불리언 (boolean) 또는 널 (null) 이다. JSON 데이터 타입 표: +----------------------------+---------------------+--------------------------------+ | 타입 | CUBRID JSON 타입 | 설명 | +============================+=====================+================================+ | Object | JSON_OBJECT | 키-값 쌍의 집합 | +----------------------------+---------------------+--------------------------------+ | Array | JSON_ARRAY | JSON 데이터 값의 배열 | +-------------+--------------+---------------------+--------------------------------+ | Scalar | String | STRING | 따옴표로 묶인 문자열 | | +--------------+---------------------+--------------------------------+ | | Number | INTEGER | 32비트의 부호 있는(signed) 숫자| | | +---------------------+--------------------------------+ | | | BIGINT | 64비트의 부호 있는(signed) 숫자| | | +---------------------+--------------------------------+ | | | DOUBLE | 정수가 아닌 숫자 또는 | | | | | 2\ :sup:`63`\ - 1 | | | | | 보다 큰 정수 | | +--------------+---------------------+--------------------------------+ | | true | BOOLEAN | True 불리언 (boolean) 값 | | +--------------+---------------------+--------------------------------+ | | false | BOOLEAN | False 불리언 (boolean) 값 | | +--------------+---------------------+--------------------------------+ | | null | JSON_NULL | Null 값 | +-------------+--------------+---------------------+--------------------------------+ JSON 값의 CUBRID JSON 타입은 :ref:`fn-json-type` 함수로 가져올 수 있다. JSON 데이터 변환 --------------------- JSON 데이터 타입은 명시적 또는 암시적으로 다른 타입으로부터 변환하여 얻을 수 있다. JSON 데이터값을 다른 JSON 타입으로 변환하는 것은 변환하려는 타입이 스키마를 가지고 있고 변환된 값이 스키마 유효성 검사를 통과하지 못하는 경우 실패할 수 있다. 다른 타입에서 JSON으로의 변환은 다음 표에서 설명한다: +----------------------------+----------------------------------------------------+ | 기존 타입 | CUBRID JSON 타입 | +============================+====================================================+ | Any string | 문자열은 모든 타입의 JSON 데이터로 변환할 수 있다. | | | | | | .. note:: | | | | | | 만약 문자열의 문자셋이 UTF8이 아니라면 | | | 문자열을 먼저 UTF8로 변환한 후 | | | JSON 데이터로 변환한다. | +----------------------------+----------------------------------------------------+ | Short, Integer | INTEGER | +----------------------------+----------------------------------------------------+ | Bigint | BIGINT | +----------------------------+----------------------------------------------------+ | Float, Double | DOUBLE | +----------------------------+----------------------------------------------------+ | Numeric | DOUBLE | +----------------------------+----------------------------------------------------+ JSON 데이터 타입에서 다른 타입으로의 변환은 다음 표에서 설명한다.: +----------------------------+-----------------------------------------------+ | CUBRID JSON 타입 | 허용되는 다른 타입 | +============================+===============================================+ | JSON_OBJECT | JSON 데이터 값을 출력한 문자열 | +----------------------------+-----------------------------------------------+ | JSON_ARRAY | JSON 데이터 값을 출력한 문자열 | +----------------------------+-----------------------------------------------+ | STRING | 문자열로부터 변환할 수 있는 모든 타입 | +----------------------------+-----------------------------------------------+ | INTEGER | 숫자값으로부터 변환할 수 있는 모든 타입 | +----------------------------+-----------------------------------------------+ | BIGINT | Bigint 값으로부터 변환할 수 있는 모든 타입 | +----------------------------+-----------------------------------------------+ | DOUBLE | Double 값으로부터 변환할 수 있는 모든 타입 | +----------------------------+-----------------------------------------------+ | BOOLEAN | 문자열로 변환된다면 "true" 또는 "false" | | +-----------------------------------------------+ | | 숫자형으로 변환된다면 0 또는 1 | +----------------------------+-----------------------------------------------+ | JSON_NULL | JSON 'null'을 출력한 문자열 | +----------------------------+-----------------------------------------------+ .. _json-path: JSON 경로 ----------- JSON 경로는 JSON 내에서 json 요소를 참조할 수 있는 방법이다. 많은 JSON 함수는 JSON 내부에서 동작 위치를 정의하기 위해 JSON 경로 또는 JSON 포인터 인수를 필요로 한다. JSON 경로는 항상 '$'로 시작하며 배열 인덱스, 객체 키 토큰 및 와일드카드가 뒤따를 수 있다. 만약 '$' 뒤에 다른 토큰이 없으면 경로는 JSON 데이터 루트를 가리킨다. :: ::= [] ... ::= $ ::= | | ::= [idx] ::= .[key_identifier | "key_str"] ::= .*|[*]|**path_token 예를 들어, '{"a":[0,1,2,{"b":5}]}'에 대해 '$.a[3].b'의 의미는 "루트의 'a'를 가진 멤버의 인덱스 3에 해당하는 요소의 키 'b'를 가진 멤버"이며 JSON 값 '5'를 가리킨다; object_key_access_tokens를 키 문자열로 사용하여 동일한 key_identifiers를 표현할 수 있으며 이스케이프 문자를 사용할 수도 있다. 예: '$"\"'은 큰따옴표를 가진 멤버를 키로 참조하는 데 사용할 수 있다. JSON 와일드카드는 다음 세 가지 유형 중 하나가 될 수 있다. - .* , 와일드카드와 매칭되는 객체 멤버 - [*], 와일드카드와 매칭되는 배열 인덱스 - \**, 객체 키의 시퀀스 배열 인덱스를 매칭. \** 와일드카드는 토큰(path_token)을 뒤에 붙여야 한다. JSON 포인터 및 JSON 텍스트와 같은 경로 식은 ASCII 또는 UTF-8 문자셋으로 인코딩 되어야 한다. 만약 다른 문자셋이 사용된다면, UTF-8으로 변환(coercion)될 것이다. .. _json-pointer: JSON 포인터 ------------- https://tools.ietf.org/html/rfc6901에서 정의한 JSON 포인터는 JSON 경로와 다른 방법을 제공한다. JSON 포인터는 JSON 경로 및 JSON 텍스트와 동일하게 ASCII 또는 UTF-8 문자셋으로 인코딩 되어야 한다. 만약 다른 문자셋이 사용된다면, UTF-8으로 변환(coercion)될 것이다. :: ::= [/path_token] ... [/-] :: '$.a[10].bb' 는 '/a/10/bb' 와 동일하다 '$' 는 '' 와 동일하다 특수 문자 '-'는 마지막 path_token으로만 사용할 수 있으며 json_array의 끝부분을 가리키는 데 사용할 수 있다. JSON 포인터는 비와일드카드 JSON 경로가 가리키는 것과 동일한 경로를 가리키는데 사용할 수 있다. .. _implicit-type-conversion: 묵시적 타입 변환 ================ 표현식 내에서 타입을 변환해야 할 때 자동으로 해당 타입으로 변환하는 것을 묵시적 타입 변환(implicit type conversion)이라고 한다. **SET**, **MULTISET**, **LIST**, **SEQUENCE** 는 명시적으로 변환되어야 한다. **DATETIME** 및 **TIMESTAMP** 타입(타임존이 있는 타입 포함)을 **DATE** 타입 또는 **TIME** 타입으로 변환하면 데이터 손실이 발생한다. **DATE** 타입을 **DATETIME** 타입 또는 **TIMESTAMP** 타입(타임존이 있는 타입 포함)으로 변환하면 시간이 '12:00:00 AM'으로 설정된다. 타임존 타입 값에서 타임존 부분은 참조용일 뿐이며, 정확한 값은 UTC 참조에 저장된다. 타임존이 있는 타입에서 타임존이 없는 타입으로 값을 변환하면 세션 타임존을 사용한 것처럼 변환된다. 타임존이 없는 타입에서 타임존이 있는 타입으로 값을 변환하면 세션 타임존을 고려하여 변환한다. 타임존 타입과 관련한 값 변환에 대한 자세한 내용은 :ref:`date-time-type` 을 참고한다. 문자열 타입이나 정확한 수치형 타입을 부동소수점 수치형 타입으로 변환하면 값이 정확하지 않을 수 있다. 문자열 타입과 정확한 수치형 타입은 값을 표현하기 위해 십진 정밀도(decimal precision)를 사용하지만 부동소수점 수치형 타입은 이진 정밀도(binary precision)를 사용하기 때문이다. CUBRID가 수행하는 묵시적 타입 변환은 다음과 같다. **묵시적 타입 변환 표 1** +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | From \\ To | DATETIME | DATETIMELTZ | DATETIMETZ | DATE | TIME | TIMESTAMP | TIMESTAMPLTZ | TIMESTAMPTZ | +==================+==============+==============+==============+==========+==========+===============+===============+===============+ | **DATETIME** | \- | O | O | O | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **DATETIMELTZ** | O | \- | O | O | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **DATETIMETZ** | O | O | \- | O | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **DATE** | O | O | O | \- | | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **TIME** | | | | | \- | | | | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **TIMESTAMP** | O | O | O | O | O | \- | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **TIMESTAMPLTZ** | O | O | O | O | O | O | \- | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **TIMESTAMPTZ** | O | O | O | O | O | O | O | \- | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **DOUBLE** | | | | | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **FLOAT** | | | | | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **NUMERIC** | | | | | | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **BIGINT** | | | | | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **INT** | | | | | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **SHORT** | | | | | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **BIT** | | | | | | | | | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **VARBIT** | | | | | | | | | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **CHAR** | O | O | O | O | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ | **VARCHAR** | O | O | O | O | O | O | O | O | +------------------+--------------+--------------+--------------+----------+----------+---------------+---------------+---------------+ .. _number-2-time: **숫자 값이 TIME 또는 TIMESTAMP(TIMESTAMPLTZ, TIMESTAMPTZ)로 변경될 때의 제약 사항** * **NUMERIC** 타입을 제외한 모든 숫자 타입은 **TIME** 타입으로 변환될 수 있으며, 이때 TIME은 입력 숫자를 86,400초(1일)로 나눈 나머지 값을 초로 계산한 값이다. * **NUMERIC**을 포함한 모든 숫자 타입은 **TIMESTAMP**, **TIMESTAMPLTZ**, **TIMESTAMPTZ** 타입으로 변환될 수 있으며, 이때 입력 숫자는 최대 2,147,483,647을 초과할 수 없다. **묵시적 타입 변환 표 2** +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | From \\ To | INT | SHORT | BIT | VARBIT | CHAR | VARCHAR | DOUBLE | FLOAT | NUMERIC | BIGINT | +==================+=========+===========+=========+============+==========+=============+============+===========+=============+============+ | **DATETIME** | | | | | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **DATETIMELTZ** | | | | | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **DATETIMETZ** | | | | | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **DATE** | | | | | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **TIME** | | | | | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **TIMESTAMP** | | | | | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **TIMESTAMPLTZ** | | | | | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **TIMESTAMPTZ** | | | | | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **DOUBLE** | O | O | | | O | O | \- | O | O | O | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **FLOAT** | O | O | | | O | O | O | \- | O | O | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **NUMERIC** | O | O | | | O | O | O | O | \- | O | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **BIGINT** | O | O | | | O | O | O | O | O | \- | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **INT** | \- | O | | | O | O | O | O | O | O | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **SHORT** | O | \- | | | O | O | O | O | O | O | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **BIT** | | | \- | O | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **VARBIT** | | | O | \- | O | O | | | | | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **CHAR** | O | O | O | O | \- | O | O | O | O | O | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ | **VARCHAR** | O | O | O | O | O | \- | O | O | O | O | +------------------+---------+-----------+---------+------------+----------+-------------+------------+-----------+-------------+------------+ 변환 규칙 --------- INSERT와 UPDATE ^^^^^^^^^^^^^^^ 영향을 받는 칼럼의 타입으로 값의 타입이 변환된다. .. code-block:: sql CREATE TABLE t(i INT); INSERT INTO t VALUES('123'); SELECT * FROM t; :: i ============= 123 함수 ^^^^ 함수에 입력한 인자 값을 함수에서 지정한 타입으로 변환할 수 있으면 인자의 타입이 변환된다. 아래 함수에서 기대하는 입력 인자는 숫자이므로, 문자열을 숫자로 변환한다. .. code-block:: sql SELECT MOD('123','2'); :: mod('123', '2') ========================== 1.000000000000000e+00 함수는 인자로 여러 타입의 값을 입력받을 수 있는데, 함수에서 지정하지 않은 타입의 값이 전달되면 다음의 우선순위에 따라 타입이 변환된다. * 날짜/시간 타입 ( **DATETIME** > **TIMESTAMP** > **DATE** > **TIME** ) * 근사치 수치형 타입 ( **DOUBLE** > **FLOAT** ) * 정확한 수치형 타입 ( **NUMERIC** > **BIGINT** > **INT** > **SHORT** ) * 문자열 타입 ( **CHAR** > **VARCHAR** ) 비교 연산 ^^^^^^^^^ 다음은 비교 연산자의 피연산자 타입에 따른 변환 규칙이다. +-----------------+-----------------+------------------------------------+----------------+ | 피연산자1 타입 | 피연산자2 타입 | 변환 | 비교 타입 | +=================+=================+====================================+================+ | 수치형 타입 | 수치형 타입 | 없음 | NUMERIC | | +-----------------+------------------------------------+----------------+ | | 문자열 타입 | 피연산자2를 **DOUBLE** 로 변환 | NUMERIC | | +-----------------+------------------------------------+----------------+ | | 날짜/시간 타입 | 피연산자1을 날짜/시간으로 변환 | TIME/TIMESTAMP | +-----------------+-----------------+------------------------------------+----------------+ | 문자열 타입 | 수치형 타입 | 피연산자1을 **DOUBLE** 로 변환 | NUMERIC | | +-----------------+------------------------------------+----------------+ | | 문자열 타입 | 없음 | 문자열 | | +-----------------+------------------------------------+----------------+ | | 날짜/시간 타입 | 피연산자1을 날짜/시간 타입으로 변환| 날짜/시간 | +-----------------+-----------------+------------------------------------+----------------+ | 날짜/시간 타입 | 수치형 타입 | 피연산자2를 날짜/시간으로 변환 | TIME/TIMESTAMP | | +-----------------+------------------------------------+----------------+ | | 문자열 타입 | 피연산자2를 날짜/시간 타입으로 변환| 날짜/시간 | | +-----------------+------------------------------------+----------------+ | | 날짜/시간 타입 | 우선순위가 높은 타입으로 변환 | 날짜/시간 | +-----------------+-----------------+------------------------------------+----------------+ 날짜/시간 타입과 수치형 타입이 비교되는 경우 위 표의 :ref:`숫자가 TIME 또는 TIMESTAMP로 변환될 때 제약 조건 `\ 을 참고한다. 피연산자1이 문자열 타입의 칼럼이고 피연산자2가 값인 경우, 다음과 같은 예외가 있다. +-----------------+-------------------+---------------------------------+----------+ | 피연산자1 타입 | 피연산자2 타입 | 변환 | 비교 | +=================+===================+=================================+==========+ | 문자열 타입의 | 수치형 타입 값 | 피연산자2를 문자열 타입으로 변환| 문자열 | | 칼럼 +-------------------+---------------------------------+----------+ | | 날짜/시간 타입 값 | 피연산자2를 문자열 타입으로 변환| 문자열 | +-----------------+-------------------+---------------------------------+----------+ 피연산자2가 집합인 연산자( **IS IN**, **IS NOT IN**, **= ALL**, **= ANY**, **< ALL**, **< ANY**, **<= ALL**, **<= ANY**, **>= ALL**, **>= ANY** )에 대해서는 위의 예외가 적용되지 않는다. 다음은 비교 연산에서 묵시적 타입 변환의 예이다. * **수치형 타입과 문자열 타입 피연산자** 문자열 타입 피연산자가 **DOUBLE** 로 변환된다. .. code-block:: sql CREATE TABLE t1(i INT, s STRING); INSERT INTO t1 VALUES(1,'1'),(2,'2'),(3,'3'),(4,'4'), (12,'12'); SELECT i FROM t1 WHERE i < '11.3'; :: i ============= 1 2 3 4 .. code-block:: sql SELECT ('2' <= 11); :: ('2'<=11) ============= 1 * **문자열 타입과 날짜/시간 타입 피연산자** 문자열 타입 피연산자가 날짜/시간 타입으로 변환된다. .. code-block:: sql SELECT ('2010-01-01' < date'2010-02-02'); :: ('2010-01-01'= '2010-01-01'); :: (date '2010-02-02'>='2010-01-01') =================================== 1 * **문자열 타입과 수치형 타입 호스트 변수 피연산자** 수치형 타입 호스트 변수가 문자열 타입으로 변환된다. .. code-block:: sql PREPARE s FROM 'SELECT s FROM t1 WHERE s < ?'; EXECUTE s USING 11; :: s =================== '1' * **문자열 타입 칼럼과 수치형 타입 값 피연산자** 수치형 타입 값이 문자열 타입으로 변환된다. .. code-block:: sql SELECT s FROM t1 WHERE s > 11; :: s ================== '2' '3' '4' '12' .. code-block:: sql SELECT s FROM t1 WHERE s BETWEEN 11 AND 33; :: s ====================== '2' '3' '12' * **문자열 타입 칼럼과 날짜/시간 타입 값 피연산자** 날짜/시간 타입 값이 문자열 타입으로 변환된다. .. code-block:: sql CREATE TABLE t2 (s STRING); INSERT INTO t2 VALUES ('01/01/1998'), ('01/01/1999'), ('01/01/2000'); SELECT s FROM t2; :: s ====================== '01/01/1998' '01/01/1999' '01/01/2000' .. code-block:: sql SELECT s FROM t2 WHERE s <= date'02/02/1998'; 위의 질의에서 date'02/02/1998'이 문자열 '02/02/1998'으로 변환되어 문자열 간의 비교 연산이 수행된다. :: s ====================== '01/01/1998' '01/01/1999' '01/01/2000' 범위 연산 ^^^^^^^^^ * **수치형 타입과 문자열 타입 피연산자** 문자열 타입 피연산자가 **DOUBLE** 로 변환된다. .. code-block:: sql CREATE TABLE t3 (i INT); INSERT INTO t3 VALUES (1), (2), (3), (4); SELECT i FROM t3 WHERE i <= ALL {'11','12'}; :: i ============= 1 2 3 4 * **문자열 타입과 날짜/시간 타입 피연산자** 문자열 타입 피연산자가 날짜/시간 타입으로 변환된다. .. code-block:: sql SELECT s FROM t2; :: s ================= '01/01/1998' '01/01/1999' '01/01/2000' .. code-block:: sql SELECT s FROM t2 WHERE s <= ALL {date'02/02/1998',date'01/01/2000'}; :: s ================ '01/01/1998' 해당 타입으로 변환할 수 없으면 오류를 반환한다. 산술 연산 ^^^^^^^^^ * **날짜/시간 타입 피연산자** 날짜/시간 타입의 피연산자가 '-' 연산자에 주어지고 타입이 서로 다르면, 두 타입을 비교하여 우선순위가 높은 쪽의 타입으로 변환된다. 다음 예는 왼쪽 피연산자의 데이터 타입이 **DATE** 에서 **DATETIME** 으로 바뀌어 결과는 **DATETIME** 의 '-' 연산 결과인 밀리초를 출력한다. .. code-block:: sql SELECT date'2002-01-01' - datetime'2001-02-02 12:00:00 am'; :: date '2002-01-01'- datetime '2001-02-02 12:00:00 am' ===================================================== 28771200000 * **수치형 타입 피연산자** 수치형 타입의 피연산자가 주어지고 타입이 서로 다르면, 두 타입을 비교하여 우선순위가 높은 쪽의 타입으로 변환된다. * **날짜/시간 타입과 수치형 타입 피연산자** 날짜/시간 타입과 수치형 타입의 피연산자가 '+' 또는 '-' 연산자에 주어지면, 수치형 타입 피연산자는 **BIGINT**, **INT**, **SHORT** 중 하나로 변환된다. * **날짜/시간 타입과 문자열 타입 피연산자** 날짜/시간 타입과 문자열 타입이 피연산자이면, '+'와 '-' 연산자만 허용한다. '+' 연산자가 사용되면 다음 규칙이 적용된다. * 문자열 타입은 인터벌(interval) 값을 지닌 **BIGINT** 로 변환된다. 인터벌은 날짜/시간 타입 피연산자의 가장 작은 단위를 의미하며, 각 타입의 인터벌 값은 다음과 같다. * **DATE** : 일수(days) * **TIME**, **TIMESTAMP** : 초수(seconds) * **DATETIME** : 밀리초수(milliseconds) * 부동소수점수는 반올림된다. * 결과 타입은 날짜/시간 타입 피연산자의 타입이다. .. code-block:: sql SELECT date'2002-01-01' + '10'; :: date '2002-01-01'+'10' ====================== 01/11/2002 날짜/시간 타입과 문자열 타입이 피연산자이고, '-' 연산자가 사용되면 다음 규칙이 적용된다. * 날짜/시간 타입 피연산자가 **DATE**, **DATETIME**, **TIMESTAMP** 이면 문자열은 **DATETIME** 으로 변환되고, 날짜/시간 타입 피연산자가 **TIME** 이면 문자열은 **TIME** 으로 변환된다. * 결과 타입은 항상 **BIGINT** 이다. .. code-block:: sql SELECT date'2002-01-01'-'2001-01-01'; :: date '2002-01-01'-'2001-01-01' ================================ 31536000000 -- this causes an error .. code-block:: sql SELECT date'2002-01-01'-'10'; :: ERROR: Cannot coerce '10' to type datetime. * **수치형 타입과 문자열 타입 피연산자** 수치형 타입과 문자열 타입이 피연산자이면 다음 규칙이 적용된다. * 문자열은 가능하면 **DOUBLE**\ 로 변환된다. * 결과 타입은 **DOUBLE**\ 이다. .. code-block:: sql SELECT 4 + '5.2'; :: 4+'5.2' ========================== 9.199999999999999e+00 CUBRID 2008 R3.1 이하 버전과 달리, 날짜/시간 형태의 문자열, 즉 '2010-09-15'와 같은 문자열은 날짜/시간 타입으로 변환되지 않는다. 날짜/시간 타입을 갖는 리터럴 DATE'2010-09-15' 은 덧셈, 뺄셈 연산에 사용할 수 있다. .. code-block:: sql SELECT '2002-01-01'+1; :: ERROR: Cannot coerce '2002-01-01' to type double. .. code-block:: sql SELECT date'2002-01-01'+1; :: date '2002-01-01'+1 ===================== 01/02/2002 * **문자열 타입 피연산자** 두 문자열을 곱하거나 나누거나 빼면 숫자로 변환되며, 결과로 **DOUBLE** 타입의 값을 반환한다. .. code-block:: sql SELECT '3'*'2'; :: '3'*'2' ============================ 6.000000000000000e+00 '+' 연산자의 동작은 **cubrid.conf** 의 시스템 파라미터인 **plus_as_concat**\ 을 어떻게 설정하느냐에 따라 결정된다. 자세한 내용은 :ref:`stmt-type-parameters`\ 를 참고한다. * **plus_as_concat** 값이 yes(기본값)이면 두 개의 문자열을 연결한 값을 반환한다. .. code-block:: sql SELECT '1'+'1'; :: '1'+'1' ====================== '11' * **plus_as_concat** 값이 no이고 두 개의 문자열이 숫자로 변환 가능하면, 두 숫자를 더하여 **DOUBLE** 타입의 값을 반환한다. .. code-block:: sql SELECT '1'+'1'; :: '1'+'1' ========================== 2.000000000000000e+00 해당 타입으로 변환할 수 없으면 오류를 반환한다.