커서 작업하기

커서를 사용하여 쿼리 결과를 한 번에 한 행씩 반복할 수 있습니다.

이 항목의 내용:

소개

쿼리 결과에서 데이터를 검색하려면 커서를 사용합니다. 루프 에서 커서를 사용하여 결과의 행을 반복할 수 있습니다.

커서를 사용하려면 다음을 수행하십시오.

  1. DECLARE 섹션에서 커서를 선언합니다. 선언에는 커서에 대한 쿼리가 포함됩니다.

  2. OPEN 명령을 실행하여 커서를 엽니다. 그러면 쿼리가 실행되고 결과가 커서에 로딩됩니다.

  3. FETCH 명령을 실행하여 하나 이상의 행을 가져오고 해당 행을 처리합니다.

  4. 결과가 끝나면 CLOSE 명령을 실행하여 커서를 닫습니다.

예에 대한 데이터 설정하기

이 섹션의 예에서는 다음 데이터를 사용합니다.

CREATE OR REPLACE TABLE invoices (id INTEGER, price NUMBER(12, 2));

INSERT INTO invoices (id, price) VALUES
  (1, 11.11),
  (2, 22.22);
Copy

커서 선언하기

SELECT 문 또는 RESULTSET 에 대한 커서를 선언할 수 있습니다.

블록의 DECLARE 섹션 또는 블록의 BEGIN … END 섹션에서 커서를 선언합니다.

  • DECLARE 섹션 내에서, 커서 선언 구문 에 설명된 구문을 사용합니다.

    예를 들어 쿼리에 대한 커서를 선언하려면:

    DECLARE
      ...
      c1 CURSOR FOR SELECT price FROM invoices;
    
    Copy

    RESULTSET에 대한 커서를 선언하려면:

    DECLARE
      ...
      res RESULTSET DEFAULT (SELECT price FROM invoices);
      c1 CURSOR FOR res;
    
    Copy
  • BEGIN … END 블록 내에서, 커서 할당 구문 에 설명된 구문을 사용합니다. 예:

    BEGIN
      ...
      LET c1 CURSOR FOR SELECT price FROM invoices;
    
    Copy

SELECT 문에서, 커서를 열 때 변수에 바인딩할 수 있는 바인딩 매개 변수(? 문자)를 지정할 수 있습니다. 변수를 매개 변수에 바인딩하려면 OPEN 명령의 USING 절에 변수를 지정합니다. 예:

DECLARE
  id INTEGER DEFAULT 0;
  minimum_price NUMBER(13,2) DEFAULT 22.00;
  maximum_price NUMBER(13,2) DEFAULT 33.00;
  c1 CURSOR FOR SELECT id FROM invoices WHERE price > ? and price < ?;
BEGIN
  OPEN c1 USING (minimum_price, maximum_price);
  FETCH c1 INTO id;
  RETURN id;
END;
Copy

참고: SnowSQL 또는 Classic Console 을 사용하는 경우 대신 다음 예를 사용하십시오(SnowSQL 및 Classic Console 에서 Snowflake Scripting 사용하기 참조).

EXECUTE IMMEDIATE $$
DECLARE
  id INTEGER DEFAULT 0;
  minimum_price NUMBER(13,2) DEFAULT 22.00;
  maximum_price NUMBER(13,2) DEFAULT 33.00;
  c1 CURSOR FOR SELECT id FROM invoices WHERE price > ? and price < ?;
BEGIN
  OPEN c1 USING (minimum_price, maximum_price);
  FETCH c1 INTO id;
  RETURN id;
END;
$$
;
Copy

커서 열기

커서를 선언하는 문이 해당 커서와 연결된 쿼리를 정의하지만, 쿼리는 OPEN 명령을 실행하여 커서를 열 때까지 실행되지 않습니다. 예:

OPEN c1;
Copy

참고

  • FOR 루프에서 커서를 사용할 때 커서를 명시적으로 열 필요가 없습니다.

  • RESULTSET 오브젝트에 대한 커서를 선언하는 경우, 오브젝트를 쿼리와 연결할 때 쿼리가 실행됩니다. 이 경우 커서를 열어도 쿼리가 다시 실행되지 않습니다.

쿼리에 바인딩 매개 변수(? 문자)가 포함된 경우 USING 절을 추가하여, 해당 매개 변수에 바인딩할 변수 목록을 지정합니다. 예:

LET c1 CURSOR FOR SELECT id FROM invoices WHERE price > ? and price < ?;
OPEN c1 USING (minimum_price, maximum_price);
Copy

커서를 열면 쿼리가 실행되고, 지정된 행을 커서로 가져오고, 첫 번째 행을 가리키는 내부 포인터를 설정합니다. FETCH 명령을 사용하여 커서를 사용해 개별 행을 가져올(읽을) 수 있습니다.

다른 SQL 쿼리와 마찬가지로, 쿼리 정의에서 가장 바깥쪽 수준에 ORDER BY가 포함되어 있지 않으면 결과 세트에는 정의된 순서가 없습니다. 커서에 대한 결과 세트가 만들어지면 커서가 닫힐 때까지 행의 순서가 유지됩니다. 커서를 다시 선언하거나 열면 행의 순서가 다를 수 있습니다. 마찬가지로, 커서를 닫고, 커서를 다시 열기 전에 기본 테이블이 업데이트되면 결과 세트도 변경될 수 있습니다.

커서를 사용하여 데이터 가져오기

FETCH 명령을 사용하여 결과 세트에서 현재 행을 검색하고, 내부 현재 행 포인터를 진행하여 결과 세트의 다음 행을 가리키도록 합니다.

INTO 절에서, 행의 값을 유지하는 데 사용해야 하는 변수를 지정합니다.

예:

FETCH c1 INTO var_for_column_value;
Copy

커서 선언의 SELECT 절에 있는 식 수와 변수 수가 일치하지 않는 경우 Snowflake는 위치별로 변수를 열과 일치시키려고 시도합니다.

  • 열보다 변수가 더 많은 경우 Snowflake는 나머지 변수를 설정하지 않은 상태로 둡니다.

  • 변수보다 열이 더 많은 경우 Snowflake는 나머지 열을 무시합니다.

실행하는 각 후속 FETCH 명령은 마지막 행을 가져올 때까지 다음 행을 가져옵니다.니다. FETCH 작업에 지연 시간이 발생할 수 있습니다. 마지막 행 다음에 행에 대한 FETCH 작업을 하려고 하면 NULL 값을 얻습니다.

RESULTSET 또는 CURSOR는 쿼리 실행 시점에서 결과 세트의 모든 행을 반드시 캐시하지는 않습니다. FETCH 작업에 지연이 발생할 수 있습니다.

커서를 사용하여 GEOGRAPHY 값 검색하기

결과에 GEOGRAPHY 유형의 열이 포함된 경우 열에 있는 값의 유형은 GEOGRAPHY가 아니라 OBJECT입니다. 즉, GEOGRAPHY 오브젝트를 입력으로 받아들이는 지리 공간적 함수 에 이 값을 직접 전달할 수 없다는 뜻입니다.

DECLARE
  geohash_value VARCHAR;
BEGIN
  LET res RESULTSET := (SELECT TO_GEOGRAPHY('POINT(1 1)') AS GEOGRAPHY_VALUE);
  LET cur CURSOR FOR res;
  FOR row_variable IN cur DO
    geohash_value := ST_GEOHASH(row_variable.geography_value);
  END FOR;
  RETURN geohash_value;
END;
Copy
001044 (42P13): Uncaught exception of type 'EXPRESSION_ERROR' on line 7 at position 21 : SQL compilation error: ...
Invalid argument types for function 'ST_GEOHASH': (OBJECT)
Copy

이 문제를 해결하려면 열 값을 GEOGRAPHY 유형으로 캐스트하십시오.

geohash_value := ST_GEOHASH(TO_GEOGRAPHY(row_variable.geography_value));
Copy

커서에 대한 테이블 반환하기

커서에서 데이터 테이블을 반환해야 하는 경우, 커서를 RESULTSET_FROM_CURSOR(cursor) 에 전달할 수 있으며, 이는 차례로 TABLE(...) 에 전달할 수 있습니다.

다음 블록은 커서에서 데이터 테이블을 반환합니다.

DECLARE
  c1 CURSOR FOR SELECT * FROM invoices;
BEGIN
  OPEN c1;
  RETURN TABLE(RESULTSET_FROM_CURSOR(c1));
END;
Copy

참고: SnowSQL 또는 Classic Console 을 사용하는 경우 대신 다음 예를 사용하십시오(SnowSQL 및 Classic Console 에서 Snowflake Scripting 사용하기 참조).

EXECUTE IMMEDIATE $$
DECLARE
  c1 CURSOR FOR SELECT * FROM invoices;
BEGIN
  OPEN c1;
  RETURN TABLE(RESULTSET_FROM_CURSOR(c1));
END;
$$
;
Copy

이 예는 다음과 같은 출력을 생성합니다.

+----+-------+
| ID | PRICE |
|----+-------|
|  1 | 11.11 |
|  2 | 22.22 |
+----+-------+
Copy

행을 가져오기 위해 이미 커서를 사용한 경우에도 RESULTSET_FROM_CURSOR 는 내부 행 포인터에서 시작하는 행뿐만 아니라 모든 행을 포함하는 RESULTSET를 반환합니다.

위에 표시된 대로 예는 첫 번째 행을 가져오고 내부 행 포인터를 두 번째 행으로 설정합니다. RESULTSET_FROM_CURSOR 는 (두 번째 행뿐만 아니라) 두 행을 모두 포함하는 RESULTSET를 반환합니다.

커서 닫기

결과 세트 작업이 끝나면 CLOSE 명령을 실행하여 커서를 닫습니다. 예:

CLOSE c1;
Copy

참고

FOR 루프에서 커서를 사용할 때 커서를 명시적으로 닫을 필요가 없습니다.

닫힌 커서에서는 FETCH 명령을 실행할 수 없습니다.

또한, 커서를 닫으면 현재 행 포인터가 무효화됩니다. 커서를 다시 열면 포인터가 새 결과 세트의 첫 번째 행을 가리킵니다.

커서 사용 예

이 예에서는 예에 대한 데이터 설정하기 에서 설정한 데이터를 사용합니다.

다음은 커서를 사용하여 두 행을 읽고 해당 행의 가격을 합산하는 저장 프로시저입니다.

DECLARE
    row_price FLOAT;
    total_price FLOAT;
    c1 CURSOR FOR SELECT price FROM invoices;
BEGIN
    row_price := 0.0;
    total_price := 0.0;
    OPEN c1;
    FETCH c1 INTO row_price;
    total_price := total_price + row_price;
    FETCH c1 INTO row_price;
    total_price := total_price + row_price;
    CLOSE c1;
    RETURN total_price;
END;
Copy

참고: SnowSQL 또는 Classic Console 을 사용하는 경우 대신 다음 예를 사용하십시오(SnowSQL 및 Classic Console 에서 Snowflake Scripting 사용하기 참조).

EXECUTE IMMEDIATE $$
DECLARE
    row_price FLOAT;
    total_price FLOAT;
    c1 CURSOR FOR SELECT price FROM invoices;
BEGIN
    row_price := 0.0;
    total_price := 0.0;
    OPEN c1;
    FETCH c1 INTO row_price;
    total_price := total_price + row_price;
    FETCH c1 INTO row_price;
    total_price := total_price + row_price;
    CLOSE c1;
    RETURN total_price;
END;
$$
;
Copy

이 예는 다음과 같은 출력을 생성합니다.

+-----------------+
| anonymous block |
|-----------------|
|           33.33 |
+-----------------+
Copy

루프를 사용하는 예는 FOR 루프 설명서에 포함되어 있습니다.

커서 문제 해결하기

증상: 커서가 모든 행이 아니라 모든 두 번째 행을 검색하는 것 같습니다.

가능한 원인:

FOR <레코드> IN <커서> 루프 내에서 FETCH 를 실행했을 수 있습니다. 커서에 대한 FOR 루프는 자동으로 다음 행을 가져옵니다. 루프 내에서 다른 가져오기를 수행하면 모든 두 번째 행을 가져옵니다.

가능한 해결책:

FOR 루프 내에서 불필요한 FETCH 를 제거합니다.

증상: FETCH 명령이 예기치 않은 NULL 값을 검색합니다.

가능한 원인:

FOR <레코드> IN <커서> 루프 내에서 FETCH 를 실행했을 수 있습니다. 커서에 대한 FOR 루프는 자동으로 다음 행을 가져옵니다. 루프 내에서 다른 가져오기를 수행하면 모든 두 번째 행을 가져옵니다. 행 수가 홀수인 경우 마지막 FETCH 는 마지막 행을 넘어 행을 가져오려고 시도하고 값은 NULL이 됩니다.

가능한 해결책:

FOR 루프 내에서 불필요한 FETCH 를 제거합니다.