산술 연산자¶
산술 연산자는 덧셈, 뺄셈, 곱셈, 나눗셈을 위한 이항(binary) 연산자와 양수, 음수를 나타내기 위한 단항(unary) 연산자가 있다. 양수/음수의 부호를 나타내는 단항 연산자의 연산 우선순위가 이항 연산자보다 높다.
<expression> <mathematical_operator> <expression>
<expression> ::=
bit_string |
character_string |
numeric_value |
date-time_value |
collection_value |
NULL
<mathematical_operator> ::=
<set_arithmetic_operator> |
<arithmetic_operator>
<arithmetic_operator> ::=
+ |
- |
* |
{ / | DIV } |
{ % | MOD }
<set_arithmetic_operator> ::=
UNION |
DIFFERENCE |
{ INTERSECT | INTERSECTION }
<expression>: 연산을 수행할 수식을 선언한다.
<mathematical_operator>: 수학적 연산을 지정하는 연산자로서, 산술 연산자와 집합 연산자가 있다.
<set_arithmetic_operator>: 컬렉션 타입의 피연산자에 대해 합집합, 차집합, 교집합을 수행하는 집합 산술 연산자이다.
<arithmetic_operator>: 사칙 연산을 수행하기 위한 연산자이다.
다음은 CUBRID가 지원하는 산술 연산자의 설명 및 리턴 값을 나타낸 표이다.
산술 연산자
산술 연산자 |
설명 |
연산식 |
리턴 값 |
---|---|---|---|
+ |
더하기 연산 |
1+2 |
3 |
- |
빼기 연산 |
1-2 |
-1 |
* |
곱하기 연산 |
1*2 |
2 |
/ |
나누기 연산 후, 몫을 반환한다. |
1/2.0 |
0.500000000 |
DIV |
나누기 연산 후, 몫을 반환한다. 피연산자는 정수 타입이어야 하며, 정수를 반환한다. |
1 DIV 2 |
0 |
% , MOD |
나누기 연산 후, 나머지를 반환한다. 피연산자는 정수 타입이어야 하며, 정수를 반환한다. 피연산자가 실수이면 MOD 함수를 이용한다. |
1 % 2 1 MOD 2 |
1 |
수치형 데이터 타입의 산술 연산과 타입 변환¶
모든 수치형 데이터 타입을 산술 연산에 사용할 수 있으며, 연산 결과 타입은 피연산자의 데이터 타입과 연산의 종류에 따라 다르다. 아래는 피연산자 타입별 덧셈/뺄셈/곱셈 연산의 결과 데이터 타입을 정리한 표이다.
피연산자의 타입별 결과 데이터 타입
INT |
NUMERIC |
FLOAT |
DOUBLE |
|
---|---|---|---|---|
INT |
INT 또는 BIGINT |
NUMERIC |
FLOAT |
DOUBLE |
NUMERIC |
NUMERIC |
NUMERIC (p와 s도 변환됨) |
DOUBLE |
DOUBLE |
FLOAT |
FLOAT |
DOUBLE |
FLOAT |
DOUBLE |
DOUBLE |
DOUBLE |
DOUBLE |
DOUBLE |
DOUBLE |
피연산자가 모두 동일한 데이터 타입이면 연산 결과의 타입이 변환되지 않으나, 나누기 연산의 경우 예외적으로 타입이 변환되므로 주의해야 한다. 분모, 즉 제수(divisor)가 0이면 에러가 발생한다.
아래는 피연산자가 모두 NUMERIC 타입인 경우, 연산 결과의 전체 자릿수(p)와 소수점 아래 자릿수(s)를 정리한 표이다.
NUMERIC 타입의 연산 결과
연산 |
결과의 최대 자릿수 |
결과의 소수점 이하 자릿수 |
---|---|---|
N(p1, s1) + N(p2, s2) |
max(p1 - s1, p2 - s2) + max(s1, s2) + 1 |
max(s1, s2) |
N(p1, s1) - N(p2, s2) |
max(p1 - s1, p2 - s2) + max(s1, s2) |
max(s1, s2) |
N(p1, s1) * N(p2, s2) |
p1 + p2 + 1 |
s1 + s2 |
N(p1, s1) / N(p2, s2) |
Pt = (p1 - s1) + s2 + max(9, max(s1, s2))
Pt = (Pt > 38) ? 38 : Pt
|
St = max(9, max(s1, s2))
St = (Pt > 38) ? min(9, St - (Pt - 38)) : St
|
예제
--int * int
SELECT 123*123;
123*123
=============
15129
-- int * int returns overflow error
SELECT (1234567890123*1234567890123);
ERROR: Data overflow on data type bigint.
-- int * numeric returns numeric type
SELECT (1234567890123*CAST(1234567890123 AS NUMERIC(15,2)));
(1234567890123* cast(1234567890123 as numeric(15,2)))
======================
1524157875322755800955129.00
-- int * float returns float type
SELECT (1234567890123*CAST(1234567890123 AS FLOAT));
(1234567890123* cast(1234567890123 as float))
===============================================
1.524158e+024
-- int * double returns double type
SELECT (1234567890123*CAST(1234567890123 AS DOUBLE));
(1234567890123* cast(1234567890123 as double))
================================================
1.524157875322756e+024
-- numeric * numeric returns numeric type
SELECT (CAST(1234567890123 AS NUMERIC(15,2))*CAST(1234567890123 AS NUMERIC(15,2)));
( cast(1234567890123 as numeric(15,2))* cast(1234567890123 as numeric(15,2)))
======================
1524157875322755800955129.0000
-- numeric * float returns double type
SELECT (CAST(1234567890123 AS NUMERIC(15,2))*CAST(1234567890123 AS FLOAT));
( cast(1234567890123 as numeric(15,2))* cast(1234567890123 as float))
=======================================================================
1.524157954716582e+024
-- numeric * double returns double type
SELECT (CAST(1234567890123 AS NUMERIC(15,2))*CAST(1234567890123 AS DOUBLE));
( cast(1234567890123 as numeric(15,2))* cast(1234567890123 as double))
========================================================================
1.524157875322756e+024
-- float * float returns float type
SELECT (CAST(1234567890123 AS FLOAT)*CAST(1234567890123 AS FLOAT));
( cast(1234567890123 as float)* cast(1234567890123 as float))
===============================================================
1.524158e+024
-- float * double returns float type
SELECT (CAST(1234567890123 AS FLOAT)*CAST(1234567890123 AS DOUBLE));
( cast(1234567890123 as float)* cast(1234567890123 as double))
================================================================
1.524157954716582e+024
-- double * double returns float type
SELECT (CAST(1234567890123 AS DOUBLE)*CAST(1234567890123 AS DOUBLE));
( cast(1234567890123 as double)* cast(1234567890123 as double))
=================================================================
1.524157875322756e+024
-- int / int returns int type without type conversion or rounding
SELECT 100100/100000;
100100/100000
===============
1
-- int / int returns int type without type conversion or rounding
SELECT 100100/200200;
100100/200200
===============
0
-- int / zero returns error
SELECT 100100/(100100-100100);
ERROR: Attempt to divide by zero.
날짜/시간 데이터 타입의 산술 연산과 타입 변환¶
피연산자가 모두 날짜/시간 데이터 타입이면 뺄셈 연산이 가능하며, 리턴 값의 타입은 BIGINT 이다. 이때 피연산자의 타입에 따라 연산 단위가 다르므로 주의한다. 날짜/시간 데이터 타입과 정수는 덧셈 및 뺄셈 연산이 가능하며, 이때 연산 단위와 리턴 값의 타입은 날짜/시간 데이터 타입을 따른다.
아래는 피연산자의 타입별로 허용하는 연산과 연산 결과의 데이터 타입을 정리한 표이다.
피연산자의 타입별 허용 연산과 결과 데이터 타입
TIME (초 단위) |
DATE (일 단위) |
TIMESTAMP (초 단위) |
DATETIME (밀리초 단위) |
INT |
|
---|---|---|---|---|---|
TIME |
뺄셈만 허용. BIGINT |
X |
X |
X |
덧셈, 뺄셈 허용. TIME |
DATE |
X |
뺄셈만 허용. BIGINT |
뺄셈만 허용. BIGINT |
뺄셈만 허용. BIGINT |
덧셈, 뺄셈 허용. DATE |
TIMESTAMP |
X |
뺄셈만 허용. BIGINT |
뺄셈만 허용. BIGINT |
뺄셈만 허용. BIGINT |
덧셈, 뺄셈 허용. TIMESTAMP |
DATETIME |
X |
뺄셈만 허용. BIGINT |
뺄셈만 허용. BIGINT |
뺄셈만 허용. BIGINT |
덧셈, 뺄셈 허용. DATETIME |
INT |
덧셈, 뺄셈 허용 TIME |
덧셈, 뺄셈 허용. DATE |
덧셈, 뺄셈 허용. TIMESTAMP |
덧셈, 뺄셈 허용. DATETIME |
모든 산술 연산 허용 |
Note
날짜/시간 산술 연산의 인자 중 하나라도 NULL 이 포함되어 있으면 수식의 결과로 NULL 이 반환된다.
예제
-- initial systimestamp value
SELECT SYSDATETIME;
SYSDATETIME
===============================
07:09:52.115 PM 01/14/2010
-- time type + 10(seconds) returns time type
SELECT (CAST (SYSDATETIME AS TIME) + 10);
( cast( SYS_DATETIME as time)+10)
====================================
07:10:02 PM
-- date type + 10 (days) returns date type
SELECT (CAST (SYSDATETIME AS DATE) + 10);
( cast( SYS_DATETIME as date)+10)
====================================
01/24/2010
-- timestamp type + 10(seconds) returns timestamp type
SELECT (CAST (SYSDATETIME AS TIMESTAMP) + 10);
( cast( SYS_DATETIME as timestamp)+10)
=========================================
07:10:02 PM 01/14/2010
-- systimestamp type + 10(milliseconds) returns systimestamp type
SELECT (SYSDATETIME + 10);
( SYS_DATETIME +10)
===============================
07:09:52.125 PM 01/14/2010
SELECT DATETIME '09/01/2009 03:30:30.001 pm'- TIMESTAMP '08/31/2009 03:30:30 pm';
datetime '09/01/2009 03:30:30.001 pm'-timestamp '08/31/2009 03:30:30 pm'
=======================================
86400001
SELECT TIMESTAMP '09/01/2009 03:30:30 pm'- TIMESTAMP '08/31/2009 03:30:30 pm';
timestamp '09/01/2009 03:30:30 pm'-timestamp '08/31/2009 03:30:30 pm'
=======================================
86400
타임존 파라미터들과 관련된 동작¶
TIMESTAMP 및 TIMESTAMP WITH LOCAL TIME ZONE 데이터 타입은 내부적으로 UNIX epoch 값(1970년 이후 경과한 시간(초))으로 저장되며, 윤초를 사용(tz_leap_second_support가 yes로 설정, 타임존 파라미터 참고)할 경우 가상 날짜-시간 값을 포함할 수 있다.
Virtual date-time Unix timestamp
2008-12-31 23:59:58 -> 79399951
2008-12-31 23:59:59 -> 79399952
2008-12-31 23:59:60 -> 79399953 -> not real date (introduced by leap second)
2009-01-01 00:00:00 -> 79399954
2009-01-01 00:00:01 -> 79399955
TIMESTAMP 및 TIMESTAMPLTZ 값이 포함된 산술 연산은 Unix epoch 값에서 바로 수행되며, 존재하지 않는 날짜/시간 값에 해당하는 Unix epoch 값이 허용된다.
SELECT TIMESTAMPLTZ'2008-12-31 23:59:59 UTC'=TIMESTAMPLTZ'2008-12-31 23:59:59 UTC'+1;
timestampltz '2008-12-31 23:59:59 UTC'=timestampltz '2008-12-31 23:59:59 UTC'+1
=================================================================================
0
따라서 위의 비교는 Unix 타임스탬프 79399952와 79399953을 비교하는 것과 같지만 동일한 값이 TIMESTAMPTZ로 사용되면 다음과 같이 동일하다.
SELECT TIMESTAMPTZ'2008-12-31 23:59:59 UTC'=TIMESTAMPTZ'2008-12-31 23:59:59 UTC'+1;
timestamptz '2008-12-31 23:59:59 UTC'=timestamptz '2008-12-31 23:59:59 UTC'+1
===============================================================================
1
화면에는 다음과 같은 불일치가 나타난다.
SELECT TIMESTAMPLTZ'2008-12-31 23:59:59 UTC'+1;
timestampltz '2008-12-31 23:59:59 UTC'+1
=============================================
11:59:59 PM 12/31/2008 Etc/UTC UTC
Unix 타임스탬프 값 79399953과 관련된 ‘2008-12-31 23:59:60 UTC는 실제 날짜가 아니기 때문에 바로 이전 값이 사용되지만 내부적으로는 ‘2008-12-31 23:59:60 UTC’ 값과 동일하다.
TIMESTAMP WITH TIME ZONE 데이터 타입은 UNIX 타임스탬프와 타임존 식별자를 모두 포함한다. UNIX 타임스탬프 부분 값에서도 TIMESTAMPTZ에 대한 연산을 수행할 수 있으나, 이 경우 자동 조정 연산이 이어서 수행된다. 지역, 오프셋, 서머타임 등의 타임존 식별자를 포함하려면 TIMESTAMPTZ 객체의 날짜-시간이 유효해야 한다. timestamptz’2008-12-31 23:59:59 UTC’+1의 경우 유효하지 않은 날짜-시간(79399953,UTC) 대신 ‘2008-12-31 23:59:59 UTC’에 해당하는 (79399952,UTC)로 자동 변환된다.
- DATETIMETZ 및 TIMESTAMPTZ를 포함하는 산술 연산 후에는, CUBRID에서 다음과 관련된 결과 값의 자동 조정을 수행한다.
타임존 식별자 조정: 타임존이 포함된 날짜에 시간(초)을 더하면 내부적으로 저장된 오프셋 규칙과 서머타임 규칙이 변경될 수 있으므로, 이에 따라 타임존 식별자를 갱신 한다.
Unix 타임스탬프 조정(TIMESTAMPTZ에만 해당): 가상의 날짜-시간 값(윤초가 활성화된 경우)이 항상 바로 이전 Unix 타임스탬프 값으로 변환된다.