카테고리:

쿼리 구문

AT | BEFORE

AT 또는 BEFORE 절은 Snowflake Time Travel에 사용됩니다. 쿼리에서 이는 테이블 이름 바로 뒤의 FROM 절에 지정되며, 오브젝트에 대해 과거 데이터가 요청되는 과거 시점을 결정합니다.

  • AT 키워드는 요청이 지정된 매개 변수와 똑같은 타임스탬프를 가진 문이나 트랜잭션에 의한 모든 변경 사항을 포함하도록 지정합니다.

  • BEFORE 키워드는 요청이 지정된 매개 변수 바로 앞의 지점을 참조하도록 지정합니다. 이 시점은 쿼리 ID로 식별되는 문이 완료되기 직전입니다. 자세한 내용은 BEFORE 절 사용하기 섹션을 참조하십시오.

자세한 내용은 Time Travel 이해 및 사용하기 섹션을 참조하십시오.

참고 항목:

FROM

구문

SELECT ...
FROM ...
  {
   AT( { TIMESTAMP => <timestamp> | OFFSET => <time_difference> | STATEMENT => <id> | STREAM => '<name>' } ) |
   BEFORE( STATEMENT => <id> )
  }
[ ... ]
Copy

매개 변수

TIMESTAMP => timestamp

Time Travel에 사용할 정확한 날짜와 시간을 지정합니다. 값은 명시적으로 TIMESTAMP, TIMESTAMP_LTZ, TIMESTAMP_NTZ 또는 TIMESTAMP_TZ 데이터 타입에 캐스팅되어야 합니다.

명시적인 캐스트를 지정하지 않으면 AT 절의 타임스탬프는 UTC 타임존(TIMESTAMP_NTZ와 동일)의 타임스탬프로 처리됩니다. 명시적 캐스트에 TIMESTAMP 데이터 타입을 사용하면 값이 TIMESTAMP_NTZ 값으로 처리될 수도 있습니다. 자세한 내용은 날짜 및 시간 데이터 타입 섹션을 참조하십시오.

OFFSET => time_difference

Time Travel에 사용할 현재 시간과의 초 단위 차이를 -N 형식으로 지정합니다. 여기서 N 은 정수 또는 산술 식일 수 있습니다(예: -120 은 120초, -30*60 은 1800초 또는 30분).

STATEMENT => id

Time Travel의 기준점으로 사용할 문의 쿼리 ID를 지정합니다. 이 매개 변수는 다음 형식 중 하나의 문을 지원합니다.

  • DML(예: INSERT, UPDATE, DELETE)

  • TCL(BEGIN, COMMIT 트랜잭션)

  • SELECT

쿼리 ID는 지난 14일 이내에 실행된 쿼리를 참조해야 합니다. 쿼리 ID가 14일이 지난 쿼리를 참조하는 경우 다음 오류가 반환됩니다.

Error: statement <query_id> not found

이 제한을 해결하려면 참조된 쿼리에 대한 타임스탬프를 사용하십시오.

STREAM => 'name'

쿼리된 테이블 또는 뷰의 기존 스트림에 대한 식별자(즉, 이름)를 지정합니다. 스트림의 현재 오프셋은 원본 오브젝트에 대한 변경 데이터를 반환하기 위한 AT 시점으로 사용됩니다.

이 키워드는 스트림을 생성하거나(CREATE STREAM 사용) 변경 데이터를 쿼리할 때만(CHANGES 절 사용) 지원됩니다. 예를 들어 다음 항목을 참조하십시오.

AT TIMESTAMP 매개 변수 사용

AT 절에서는 TIMESTAMP 키워드 다음에 타임스탬프를 나타내는 문자열과 TIMESTAMP, TIMESTAMP_TZ, TIMESTAMP_LTZ 또는 TIMESTAMP_NTZ 데이터 타입으로 명시적으로 캐스팅하는 옵션을 지정할 수 있습니다. 다음 예는 모두 유효합니다.

AT ( TIMESTAMP => '2024-06-05 12:30:00'::TIMESTAMP_LTZ )

AT ( TIMESTAMP => '2024-06-05 12:30:00'::TIMESTAMP )

AT ( TIMESTAMP => '2024-06-05 12:30:00' )
Copy

명시적인 캐스트를 지정하지 않으면 AT 절의 타임스탬프는 UTC 타임존(TIMESTAMP_NTZ와 동일)의 타임스탬프로 처리됩니다. 날짜 및 시간 데이터 타입 의 설명대로, 명시적 캐스트에 TIMESTAMP 데이터 타입을 사용하면 값이 TIMESTAMP_NTZ 값으로 처리될 수도 있습니다.

타임스탬프는 세션의 현재 타임존과 TIMESTAMP_TYPE_MAPPING 매개 변수 값을 기준으로 해석되므로 자신이 선택한 명시적 캐스트가 Time Travel 쿼리의 결과에 영향을 미칩니다. 이 동작에 대한 자세한 내용은 UTC 이외의 타임존을 사용하는 세션에서 Time Travel 데이터 쿼리 를 참조하십시오.

현재 타임존이 America/Los_Angeles 이고 TIMESTAMP_TYPE_MAPPING이 TIMESTAMP_NTZ 로 설정된 SQL 세션에서 쿼리를 실행 중인 상황을 예로 들겠습니다. 다음과 같이 테이블을 만들고 즉시 행을 2개 삽입합니다.

CREATE OR REPLACE TABLE tt1 (c1 INT, c2 INT);
INSERT INTO tt1 VALUES(1,2);
INSERT INTO tt1 VALUES(2,3);
Copy

SHOW TABLES 명령으로 테이블 생성 시간을 확인합니다.

SHOW TERSE TABLES LIKE 'tt1';
Copy
+-------------------------------+------+-------+---------------+----------------+
| created_on                    | name | kind  | database_name | schema_name    |
|-------------------------------+------+-------+---------------+----------------|
| 2024-06-05 15:25:35.557 -0700 | TT1  | TABLE | TRAVEL_DB     | TRAVEL_SCHEMA  |
+-------------------------------+------+-------+---------------+----------------+

created_on 열의 타임존 오프셋에 유의하십시오. 5분 후에 다른 행을 삽입합니다.

INSERT INTO tt1 VALUES(3,4);
Copy

이제 처음 두 행이 반환되기를 기대하며 다음 Time Travel 쿼리를 실행합니다.

SELECT * FROM tt1 at(TIMESTAMP => '2024-06-05 15:29:00'::TIMESTAMP);
Copy
000707 (02000): Time travel data is not available for table TT1. The requested time is either beyond the allowed time travel period or before the object creation time.

세션의 타임존이 UTC이고 TIMESTAMP에 대한 명시적 캐스트가 해당 타임존을 따르므로 쿼리가 실패합니다. 따라서 테이블은 지정된 타임스탬프 이후 에 생성된 것으로 간주합니다. 이 문제를 해결하려면 TIMESTAMP_LTZ(현지 타임존)에 대한 명시적 캐스트로 쿼리를 다시 실행합니다.

SELECT * FROM tt1 at(TIMESTAMP => '2024-06-05 15:29:00'::TIMESTAMP_LTZ);
Copy
+----+----+
| C1 | C2 |
|----+----|
|  1 |  2 |
|  2 |  3 |
+----+----+

예상대로, 이 쿼리는 삽입된 첫 2개 행을 반환합니다. 마지막으로, 동일한 쿼리를 실행하지만 약간 더 늦은 타임스탬프를 지정합니다.

SELECT * FROM tt1 at(TIMESTAMP => '2024-06-05 15:31:00'::TIMESTAMP_LTZ);
Copy
+----+----+
| C1 | C2 |
|----+----|
|  1 |  2 |
|  2 |  3 |
|  3 |  4 |
+----+----+

이 쿼리는 더 늦은 타임스탬프를 고려하여 3개의 행을 모두 반환합니다.

BEFORE 절 사용하기

BEFORE 절의 STATEMENT 매개 변수는 쿼리 ID를 참조해야 합니다. Time Travel이 사용하는 과거 시점은 해당 쿼리 ID에 대한 문이 시작되기 전이 아니라 문이 완료되기 직전입니다. 동시 쿼리가 명령문의 시작과 끝 사이에 데이터에 대한 수정 사항을 커밋하는 경우 이러한 변경 사항은 결과에 포함됩니다.

예를 들어, 다음 문은 테이블 my_table 에서 두 개의 개별 스레드에서 병렬로 실행되고 있습니다.

시간

스레드

작업

단계

설명

t1

1

INSERT INTO my_table(id) VALUE(1)

시작

Insert는 필요한 검사를 수행하여 실행을 시작합니다.

t2

1

INSERT INTO my_table(id) VALUE(1)

종료

업데이트된 my_table 을 삽입합니다.

t3

1

DELETE FROM my_table

시작

Delete는 삭제할 레코드 목록을 식별합니다(id=1).

t4

2

INSERT INTO my_table(id) VALUE(2)

시작

Insert는 필요한 검사를 수행하여 실행을 시작합니다.

t5

2

INSERT INTO my_table(id) VALUE(2)

종료

업데이트된 my_table 을 삽입합니다.

t6

2

SELECT * FROM my_table

종료

스레드 2my_table 에서 행을 선택합니다. 결과에는 모든 행(id=1, id=2)이 포함됩니다.

t7

1

DELETE FROM my_table

종료

스레드 1 (id=1)에서 삭제 문이 시작된 시간 t3 이전에 존재하는 모든 이전 레코드를 삭제하는 업데이트 my_table 을 삭제합니다.

t8

1

SELECT * FROM my_table BEFORE(STATEMENT => LAST_QUERY_ID())

종료

SELECT 문은 Time Travel을 사용하여 삭제 작업이 완료되기 전의 기록 데이터를 검색합니다. 결과에는 스레드 2 (id=1, id=2)에서 동시에 발생한 두 번째 삽입 문의 행이 포함됩니다.

해결 방법으로 문이 시작되기 직전의 시점을 지정하는 TIMESTAMP 매개 변수를 사용할 수 있습니다.

사용법 노트

  • Snowflake의 데이터는 시스템 시간의 정확한 값과는 약간 다를 수 있는 타임스탬프로 식별됩니다.

  • TIMESTAMP 또는 OFFSET의 값은 상수 식이어야 합니다.

  • TIMESTAMP의 최소 시간 해상도는 밀리초입니다.

  • 요청된 데이터가 Time Travel 보존 기간(기본값은 1일)을 초과하는 경우, 문이 실패합니다.

    또한, 요청된 데이터가 Time Travel 보존 기간 내에 있지만, 사용 가능한 과거 데이터가 없는 경우(예: 보존 기간이 연장된 경우), 문이 실패합니다.

  • 지정된 Time Travel 시간이 오브젝트의 생성 시점 또는 그 이전인 경우 문이 실패합니다. AT TIMESTAMP 매개 변수 사용 섹션을 참조하십시오.

  • 기록 테이블 데이터에 액세스하면 테이블의 현재 정의에 있는 열, 기본값 등이 결과에 포함됩니다. 구체화되지 않은 뷰에도 동일하게 적용됩니다. 예를 들어 테이블을 변경하여 열을 추가하는 경우 열이 추가된 시점 이전의 기록 데이터를 쿼리하면 새 열이 포함된 결과가 반환됩니다.

  • 과거 데이터에는 현재 데이터와 동일한 액세스 제어 요구 사항이 있습니다. 모든 변경 사항은 소급 적용됩니다.

  • AT 및 BEFORE 절은 CTE 에서 기록 데이터 선택을 지원하지 않습니다.

    예를 들어, 다음 쿼리는 지원되지 않습니다.

    WITH mycte AS
      (SELECT mytable.* FROM mytable)
    SELECT * FROM mycte AT(TIMESTAMP => '2024-03-13 13:56:09.553 +0100'::TIMESTAMP_TZ);
    
    Copy

    그러나 이러한 절은 WITH 절의 쿼리에서 지원됩니다. 예를 들어, 다음 쿼리는 지원됩니다.

    WITH mycte AS
      (SELECT * FROM mytable AT(TIMESTAMP => '2024-03-13 13:56:09.553 +0100'::TIMESTAMP_TZ))
    SELECT * FROM mycte;
    
    Copy
  • 하이브리드 테이블에 대한 Time Travel 쿼리에는 다음과 같은 제한이 있습니다.

    • AT 절에서는 TIMESTAMP 매개 변수만 지원됩니다. OFFSET, STATEMENT 및 STREAM 매개 변수는 지원되지 않습니다.

    • TIMESTAMP 매개 변수의 값은 동일한 데이터베이스에 속한 모든 테이블에서 동일해야 합니다. 테이블이 서로 다른 데이터베이스에 속한 경우 다른 TIMESTAMP 값을 사용할 수 있습니다.

    • BEFORE 절은 지원되지 않습니다.

문제 해결하기

오류

Time travel data is not available for table <tablename>

원인

어떤 경우에는 이는 타임스탬프가 필요한 문자열을 사용하여 발생합니다.

해결책

문자열을 타임스탬프로 캐스팅합니다.

... AT(TIMESTAMP => '2018-07-27 12:00:00')               -- fails
... AT(TIMESTAMP => '2018-07-27 12:00:00'::TIMESTAMP)    -- succeeds
Copy

특정 타임스탬프를 사용하여 테이블에서 과거 데이터를 선택합니다. TIMESTAMP 매개 변수를 사용하는 처음 두 예에서 my_table 은 표준 테이블 또는 하이브리드 테이블일 수 있습니다.

SELECT * FROM my_table AT(TIMESTAMP => 'Wed, 26 Jun 2024 09:20:00 -0700'::TIMESTAMP_LTZ);
Copy
SELECT * FROM my_table AT(TIMESTAMP => TO_TIMESTAMP(1432669154242, 3));
Copy

5분 전 시점의 테이블에서 과거 데이터를 선택합니다.

SELECT * FROM my_table AT(OFFSET => -60*5) AS T WHERE T.flag = 'valid';
Copy

테이블에서부터, 지정된 트랜잭션에 의해 변경된 사항(해당 변경 사항 제외)까지의 과거 데이터를 선택합니다.

SELECT * FROM my_table BEFORE(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726');
Copy

지정된 트랜잭션으로 인한 테이블 데이터의 차이를 반환합니다.

SELECT oldt.* ,newt.*
  FROM my_table BEFORE(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726') AS oldt
    FULL OUTER JOIN my_table AT(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726') AS newt
    ON oldt.id = newt.id
  WHERE oldt.id IS NULL OR newt.id IS NULL;
Copy

다음 예에서는 동일한 데이터베이스의 두 테이블에 대해 Time Travel 조인 쿼리를 실행하는데, 그중 하나는 하이브리드 테이블입니다. 두 테이블 모두에 동일한 TIMESTAMP 식을 사용해야 합니다.

SELECT *
  FROM db1.public.htt1
    AT(TIMESTAMP => '2024-06-05 17:50:00'::TIMESTAMP_LTZ) h
    JOIN db1.public.tt1
    AT(TIMESTAMP => '2024-06-05 17:50:00'::TIMESTAMP_LTZ) t
    ON h.c1=t.c1;
Copy