PERIOD 데이터 타입

PERIOD 데이터 타입 (PERIOD data type)

Snowflake의 PERIOD 데이터 타입을 다루는 문서예요. PERIOD는 동일한 요소 타입의 시작 경계와 끝 경계를 단일 값으로 저장하는, 고정된 시간 범위(anchored temporal range)를 나타내요. 반개방 구간 [begin, end) 의미론을 사용하며, 고용 기간이나 버전 유효 간격 같은 특정 시간 범위를 식별해요. 타입 지정, 경계 의미론, 생성·접근·정렬·변환, 데이터 거버넌스와 예제를 확인할 수 있어요.

출처: Snowflake SQL Reference

본문

PERIOD 데이터 타입은 고정된 시간 범위를 단일 값으로 저장해요: 동일한 요소 타입의 시작 경계(beginning bound)와 끝 경계(ending bound). 시간상 고정 위치 없이 지속 시간을 나타내는 인터벌 데이터 타입(interval data types)과 달리, PERIOD 값은 타임라인에서 특정 범위(예: 고용 기간 또는 버전 유효 간격)를 식별해요.

PERIOD 값은 반개방 구간(half-open interval) 의미론 [begin, end)을 사용해요: 시작 경계는 포함되고 끝 경계는 제외돼요. 범위는 시작 경계에서 시작해 끝 경계까지(끝 경계는 미포함) 확장돼요. 빈 period는 허용되지 않으며, 생성자와 캐스트는 begin < end를 요구해요.

참고: 이 문서는 PERIOD 데이터 타입(일급 열 타입)을 다뤄요. 시간 버전 관리를 위해 두 개의 기존 스칼라 열을 논리적 period로 묶는 SQL:2011 PERIOD FOR 테이블 수준 절은 다루지 않아요. 그 절은 지원되지 않아요.

PERIOD 데이터 타입 지정하기

PERIOD 타입을 지정하려면 다음 구문을 사용해요.

PERIOD( <element_type> )

여기서 _element_type_은 다음 중 하나예요.

  • DATE
  • TIME [ (<scale>) ]
  • TIMESTAMP_NTZ [ (<scale>) ]
  • TIMESTAMP_LTZ [ (<scale>) ]
  • TIMESTAMP_TZ [ (<scale>) ]

TIME과 TIMESTAMP 요소 타입의 경우, 0에서 9 사이의 scale을 지정할 수 있어요. scale을 생략하면 Snowflake는 해당 시간 타입의 기본 scale을 사용해요.

요소 타입을 반드시 지정해야 해요. 요소 타입 없는 맨 PERIOD는 유효한 열이나 표현식 타입이 아니에요.

유효한 타입 정의의 예:

PERIOD(DATE)
PERIOD(TIME(0))
PERIOD(TIMESTAMP_NTZ(9))
PERIOD(TIMESTAMP_LTZ)
PERIOD(TIMESTAMP_TZ(6))

경계 의미론 (Bound semantics)

모든 PERIOD 값은 반개방 범위 [begin, end)예요.

  • 순간 begin은 period에 포함돼요.
  • 순간 end는 period에서 제외돼요.
  • [a, b)[b, c) 같은 인접한 period는 겹치지 않고 b에서 만나요.

두 경계 모두 유한해야 해요. PERIOD는 무한 범위, 무한대 리터럴, 또는 NULL-무한 의미론을 지원하지 않아요. 열린 범위(예: "여전히 활성")를 표현하려면 먼 미래의 끝 경계 같은 규칙을 사용해요.

PERIOD_CONSTRUCT(hire_date, DATE '9999-12-31')

Snowflake는 PostgreSQL 스타일의 설정 가능한 괄호 포함성([], (), (])을 노출하지 않아요. 반개방 [begin, end)이 유일한 저장 형태예요.

PERIOD 값 생성하기 (Construct PERIOD values)

PERIOD 값은 다음 방법으로 만들 수 있어요.

PERIOD_CONSTRUCT

PERIOD_CONSTRUCT는 동일한 요소 타입의 두 시간 표현식에서 PERIOD를 만들어요.

SELECT PERIOD_CONSTRUCT(DATE '2024-01-01', DATE '2024-12-31');

begin >= end이면 Snowflake는 오류를 발생시켜요.

타입 리터럴 (Typed literal)

파라미터화된 ANSI 리터럴 형식을 사용해요.

SELECT PERIOD(DATE) '[2024-01-01, 2024-12-31)';

경계는 요소 타입의 세션 입력 형식(예: DATE 경계의 DATE_INPUT_FORMAT)에 따라 파싱돼요.

텍스트에서의 명시적 캐스트 (Explicit cast from text)

정규 [begin, end) 형식의 문자열을 PERIOD 타입으로 캐스팅해요.

SELECT '[2024-01-01, 2024-12-31)'::PERIOD(DATE);

SELECT TRY_CAST('[2024-01-01, 2024-12-31)' AS PERIOD(DATE));

TRY_CAST는 잘못된 문자열이나 유효하지 않은 경계(begin >= end)에 대해 오류를 발생시키는 대신 NULL을 반환해요.

열 타입의 텍스트 표현을 허용하는 DML 컨텍스트 밖에서는 TEXT에서 PERIOD로의 암시적 강제 변환(coercion)이 없어요.

PERIOD 경계 접근하기 (Access PERIOD bounds)

접근자 함수를 사용해 타입이 지정된 begin과 end 값을 추출해요.

SELECT
    PERIOD_BEGIN(active_period) AS begin_bound,
    PERIOD_END(active_period) AS end_bound
  FROM employment_windows;

결과 타입은 PERIOD 요소 타입(DATE, TIME, 또는 해당 TIMESTAMP 변형)과 일치해요. 전체 작업 예제는 "테이블을 만들고 PERIOD 값 쿼리하기"를 참조하세요.

정렬과 비교 (Ordering and comparison)

PERIOD 값은 정렬 가능하고 해시 가능해요. 정렬은 begin 우선, 그 다음 end예요.

  • PERIOD_BEGIN(p1) < PERIOD_BEGIN(p2)이거나, 시작 경계가 같고 PERIOD_END(p1) < PERIOD_END(p2)이면 p1 < p2.

PERIOD 열은 ORDER BY, GROUP BY, SELECT DISTINCT에 사용할 수 있고 COUNT로 셀 수 있어요. MIN, MAX, SUM, AVG 같은 집계 함수는 PERIOD에서 지원되지 않아요.

두 PERIOD 값의 동등성(equality)은 두 경계를 모두 비교해요. overlaps, contains 같은 술어 도우미는 Period 함수(Period functions)를 참조하세요.

변환 (Conversion)

PERIOD 값으로 변환하기

다음에서 PERIOD로 명시적으로 캐스팅할 수 있어요.

  • TEXT / VARCHAR (정규 [begin, end) 문자열)
  • NULL (표준 null 처리)

요소 간 PERIOD 캐스트(예: PERIOD(DATE)에서 PERIOD(TIMESTAMP_NTZ))는 명시적 캐스트를 요구해요. 서로 다른 PERIOD 요소 타입 사이에는 암시적 강제 변환이 없어요.

PERIOD 값에서 변환하기

PERIOD 값을 TEXT / VARCHAR로 명시적으로 캐스팅할 수 있어요. 결과는 요소 타입의 세션 출력 형식을 사용한 정규 문자열 형식 [begin, end)이에요.

SELECT TO_CHAR(PERIOD(DATE) '[2024-01-01, 2024-12-31)') AS period_text;
+--------------------------+
| PERIOD_TEXT              |
|--------------------------|
| [2024-01-01, 2024-12-31) |
+--------------------------+

드라이버는 현재 PERIOD 값을 그 정규 형식의 텍스트로 받아요.

PERIOD 데이터 로딩과 언로딩 (Load and unload PERIOD data)

PERIOD 열을 텍스트로(예: CSV 또는 JSON 문자열 필드) 로딩·언로딩할 때 정규 형식을 사용해요.

  • JSON: "[2024-01-01, 2024-12-31)" 같은 따옴표 문자열
  • CSV: [2024-01-01, 2024-12-31) 같은 따옴표 또는 비따옴표 문자열

PERIOD는 네이티브 Parquet 또는 Iceberg 타입이 아니에요. 관리형 Iceberg 테이블에 PERIOD 열을 만드는 것은 지원되지 않아요.

데이터 거버넌스 (Data governance)

마스킹 정책(masking policy) 또는 행 접근 정책(row access policy)은 다른 타입의 열을 보호하는 것과 같은 방식으로 PERIOD 열을 보호해요. 다음 동작은 PERIOD에 특정한 것이에요.

마스킹 정책 요소 타입이 일치해야 함 (Masking policy element type must match)

마스킹 정책 시그니처는 열 데이터 타입과 일치해야 해요. PERIOD 열의 경우 이 일치에는 요소 타입이 포함돼요: 열과 동일한 PERIOD 요소 타입으로 정책을 정의하고, 입력 인자와 반환 값 모두에 그 타입을 사용해요.

예를 들어 다음 마스킹 정책은 PERIOD(DATE) 열에 적용돼요.

CREATE MASKING POLICY period_date_mask AS (val PERIOD(DATE))
  RETURNS PERIOD(DATE) ->
    CASE
      WHEN CURRENT_ROLE() = 'ANALYST' THEN val
      ELSE PERIOD(DATE) '[1970-01-01, 1970-01-02)'
    END;

ALTER TABLE … ALTER COLUMN(SET MASKING POLICY)으로 열에 마스킹 정책을 직접 연결할 때, 요소 타입이 열과 일치하지 않는 정책은 Snowflake가 거부해요. 예를 들어 PERIOD(DATE) 정책을 PERIOD(TIME) 열에 연결하면 오류가 반환돼요.

COLUMN data type PERIOD(TIME(9)) does not match with masking policy data type PERIOD.

태그 기반 마스킹(tag-based masking)으로 태그는 PERIOD 요소 타입마다 마스킹 정책 하나를 담을 수 있어요. 예를 들어 같은 태그에 PERIOD(DATE) 정책과 PERIOD(TIME) 정책을 모두 할당할 수 있고, 각 태그된 열은 요소 타입과 일치하는 정책으로 보호돼요. 모든 데이터 타입과 마찬가지로 태그의 정책은 타입이 일치할 때만 열을 보호해요: 태그된 열의 요소 타입이 태그의 어떤 마스킹 정책과도 일치하지 않으면 그 열은 마스킹되지 않아요.

현재 제한 사항 (Current limitations)

PERIOD 데이터 타입에는 다음 제한 사항이 적용돼요.

  • 요소 타입 없는 맨 PERIOD는 열이나 표현식 타입으로 지원되지 않아요.
  • 무한 period와 Teradata 스타일의 UNTIL_CLOSED / UNTIL_CHANGED 센티널(sentinel)은 지원되지 않아요.
  • SQL:2011 PERIOD FOR 테이블 선언은 지원되지 않아요.
  • PERIOD 열은 Iceberg 테이블에서 지원되지 않아요.
  • 클라이언트 드라이버는 PERIOD를 텍스트로 직렬화해요; 정형 begin/end 클라이언트 타입은 아직 사용할 수 없어요.
  • SQL이 아닌 UDF, SQL 이외 언어의 저장 프로시저, Snowpark에서의 지원은 제한적이거나 연기돼요. PERIOD 표현식에는 SQL을 사용하는 것을 권장해요.
  • PERIOD를 INTERVAL 지속 시간만큼 이동시키는 인터벌 산술은 아직 지원되지 않아요.
  • VARIANT(및 관련 반정형 임베딩) 안에 PERIOD 값을 저장하는 것은 연기돼요.

PERIOD 함수 (PERIOD functions)

Snowflake는 PERIOD 값을 위한 생성자, 접근자, 술어, 집합 연산 함수를 제공해요. 전체 목록은 Period 함수(Period functions)를 참조하세요.

PERIOD 데이터 타입 예제 (Examples)

다음 예제는 PERIOD 값을 저장·생성·비교·결합하는 방법을 보여줘요.

  • 테이블을 만들고 PERIOD 값 쿼리하기
  • PERIOD 값을 안전하게 생성하기
  • 반개방 경계 의미론 다루기
  • 술어로 관계 테스트하기
  • 집합 연산으로 period 결합하기
  • PERIOD 값 정렬하기
  • TIMESTAMP 요소 타입 사용하기

테이블을 만들고 PERIOD 값 쿼리하기

  1. PERIOD(DATE) 열이 있는 테이블을 만들어요.
CREATE OR REPLACE TABLE employment_windows (
  employee_id INTEGER,
  active_period PERIOD(DATE)
);
  1. 타입 리터럴, 텍스트 캐스트, PERIOD_CONSTRUCT 세 가지 생성 스타일로 행을 삽입해요.
INSERT INTO employment_windows VALUES
  (1, PERIOD(DATE) '[2020-01-15, 2023-06-30)'),
  (2, '[2021-03-01, 9999-12-31)'::PERIOD(DATE)),
  (3, PERIOD_CONSTRUCT(DATE '2022-09-01', DATE '2024-01-01'));
  1. 접근자 함수로 타입이 지정된 경계를 쿼리하고, TO_CHAR로 정규 텍스트 형식을 표시해요.
SELECT
    employee_id,
    PERIOD_BEGIN(active_period) AS start_date,
    PERIOD_END(active_period) AS end_date,
    TO_CHAR(active_period) AS period_text
  FROM employment_windows
  ORDER BY employee_id;
+-------------+------------+------------+--------------------------+
| EMPLOYEE_ID | START_DATE | END_DATE   | PERIOD_TEXT              |
|-------------+------------+------------+--------------------------|
|           1 | 2020-01-15 | 2023-06-30 | [2020-01-15, 2023-06-30) |
|           2 | 2021-03-01 | 9999-12-31 | [2021-03-01, 9999-12-31) |
|           3 | 2022-09-01 | 2024-01-01 | [2022-09-01, 2024-01-01) |
+-------------+------------+------------+--------------------------+
  1. VARCHAR로 캐스팅해 전체 열을 정규 형식으로 표시할 수도 있어요.
SELECT employee_id, active_period::VARCHAR AS period_varchar
  FROM employment_windows
  ORDER BY employee_id;
+-------------+--------------------------+
| EMPLOYEE_ID | PERIOD_VARCHAR           |
|-------------+--------------------------|
|           1 | [2020-01-15, 2023-06-30) |
|           2 | [2021-03-01, 9999-12-31) |
|           3 | [2022-09-01, 2024-01-01) |
+-------------+--------------------------+

PERIOD 값을 안전하게 생성하기 (Construct PERIOD values safely)

이전 예제에서처럼 타입 리터럴, 텍스트 캐스트, PERIOD_CONSTRUCT로 동일한 PERIOD 값을 만들 수 있어요. 신뢰할 수 없는 텍스트를 파싱할 때는 TRY_CAST를 사용해 잘못된 문자열이나 유효하지 않은 경계(begin >= end)가 오류 대신 NULL을 반환하도록 해요.

SELECT
    TRY_CAST('[2024-01-01, 2024-12-31)' AS PERIOD(DATE)) AS valid_bounds,
    TRY_CAST('[2024-12-31, 2024-01-01)' AS PERIOD(DATE)) AS invalid_bounds;
+--------------------------+----------------+
| VALID_BOUNDS             | INVALID_BOUNDS |
|--------------------------+----------------|
| [2024-01-01, 2024-12-31) | NULL           |
+--------------------------+----------------+

반개방 경계 의미론 다루기 (Work with half-open bound semantics)

PERIOD는 반개방 [begin, end)이므로 시작 경계는 period에 포함되지만 끝 경계는 포함되지 않아요.

SELECT
    PERIOD_CONTAINS(PERIOD(DATE) '[2024-01-01, 2024-04-01)', DATE '2024-01-01') AS begin_inclusive,
    PERIOD_CONTAINS(PERIOD(DATE) '[2024-01-01, 2024-04-01)', DATE '2024-04-01') AS end_exclusive;
+-----------------+---------------+
| BEGIN_INCLUSIVE | END_EXCLUSIVE |
|-----------------+---------------|
| True            | False         |
+-----------------+---------------+

술어로 관계 테스트하기 (Test relationships with predicates)

술어 함수는 두 period가 서로 어떻게 관련되는지 테스트해요. 이 예제는 세 개의 period에 대해 overlap(겹침), adjacency(인접), containment(포함)를 확인해요.

WITH periods AS (
  SELECT
    PERIOD(DATE) '[2024-01-01, 2024-04-01)' AS p1,
    PERIOD(DATE) '[2024-03-01, 2024-06-01)' AS p2,
    PERIOD(DATE) '[2024-04-01, 2024-07-01)' AS p3
)
SELECT
    PERIOD_OVERLAPS(p1, p2) AS p1_overlaps_p2,
    PERIOD_OVERLAPS(p1, p3) AS p1_overlaps_p3,
    PERIOD_MEETS(p1, p3) AS p1_meets_p3,
    PERIOD_CONTAINS(p1, DATE '2024-01-01') AS contains_begin,
    PERIOD_CONTAINS(p1, DATE '2024-04-01') AS contains_end
  FROM periods;
+----------------+----------------+-------------+----------------+--------------+
| P1_OVERLAPS_P2 | P1_OVERLAPS_P3 | P1_MEETS_P3 | CONTAINS_BEGIN | CONTAINS_END |
|----------------+----------------+-------------+----------------+--------------|
| True           | False          | True        | True           | False        |
+----------------+----------------+-------------+----------------+--------------+

공유 경계에서 만나는 인접 period는 겹치지 않아요. 공유 순간은 이전 period의 제외 끝 경계이자 이후 period의 포함 시작 경계이기 때문이에요.

집합 연산으로 period 결합하기 (Combine periods with set operations)

PERIOD_INTERSECT로 두 period의 겹치는 하위 범위를 반환해요.

SELECT PERIOD_INTERSECT(
    PERIOD(DATE) '[2024-01-01, 2024-07-01)',
    PERIOD(DATE) '[2024-04-01, 2024-10-01)'
  ) AS overlap;
+--------------------------+
| OVERLAP                  |
|--------------------------|
| [2024-04-01, 2024-07-01) |
+--------------------------+

인자가 분리되어 있으면 PERIOD_INTERSECT는 NULL을 반환해요.

PERIOD_LDIFFPERIOD_RDIFF로 첫 period에서 두 번째 period보다 앞이나 뒤에 있는 부분을 반환해요.

SELECT
    PERIOD_LDIFF(
      PERIOD(DATE) '[2024-01-01, 2024-07-01)',
      PERIOD(DATE) '[2024-04-01, 2024-10-01)'
    ) AS left_remainder,
    PERIOD_RDIFF(
      PERIOD(DATE) '[2024-01-01, 2024-07-01)',
      PERIOD(DATE) '[2024-01-01, 2024-04-01)'
    ) AS right_remainder;
+--------------------------+--------------------------+
| LEFT_REMAINDER           | RIGHT_REMAINDER          |
|--------------------------+--------------------------|
| [2024-01-01, 2024-04-01) | [2024-04-01, 2024-07-01) |
+--------------------------+--------------------------+

두 번째 period가 첫 period의 해당 측면을 자르지 않으면 이 함수들은 NULL을 반환해요.

PERIOD 값 정렬하기 (Order PERIOD values)

PERIOD 값은 begin 우선, 그 다음 end로 정렬돼요. 다음 쿼리는 시작 경계나 시작 월을 공유하는 세 개의 period를 정렬해요.

SELECT p::VARCHAR AS p
  FROM (
    SELECT PERIOD(DATE) '[2024-01-01, 2024-06-01)' AS p
    UNION ALL SELECT PERIOD(DATE) '[2024-02-01, 2024-03-01)'
    UNION ALL SELECT PERIOD(DATE) '[2024-01-01, 2024-12-31)'
  )
  ORDER BY p;
+--------------------------+
| P                        |
|--------------------------|
| [2024-01-01, 2024-06-01) |
| [2024-01-01, 2024-12-31) |
| [2024-02-01, 2024-03-01) |
+--------------------------+

2024-01-01에 시작하는 두 period는 끝 경계로 정렬되고, 2024-02-01에 시작하는 period는 마지막으로 정렬돼요.

TIMESTAMP 요소 타입 사용하기 (Use a TIMESTAMP element type)

PERIOD는 날짜에만 국한되지 않아요. 이 예제는 근무 교대를 PERIOD(TIMESTAMP_NTZ) 값으로 만든다.

SELECT PERIOD(TIMESTAMP_NTZ) '[2024-01-01 09:00:00, 2024-01-01 17:00:00)' AS shift;
+----------------------------------------------------------------+
| SHIFT                                                          |
|----------------------------------------------------------------|
| [2024-01-01 09:00:00.000000000, 2024-01-01 17:00:00.000000000) |
+----------------------------------------------------------------+

더 알아보기 (Learn more)