루프 작업하기

Snowflake Scripting은 다음 타입의 루프를 지원합니다.

이 항목에서는 이러한 형식의 루프를 각각 사용하는 방법에 대해 설명합니다.

FOR 루프

FOR 루프는 지정된 횟수만큼 또는 결과 세트의 각 행에 대해 일련의 스테이지를 반복합니다. Snowflake Scripting은 다음 타입의 FOR 루프를 지원합니다.

다음 섹션에서는 이러한 타입의 FOR 루프를 사용하는 방법을 설명합니다.

카운터 기반 FOR 루프

카운터 기반 FOR 루프는 지정된 횟수만큼 실행합니다.

카운터 기반 FOR 루프에는 다음 구문을 사용합니다.

FOR <counter_variable> IN [ REVERSE ] <start> TO <end> { DO | LOOP }
  <statement>;
  [ <statement>; ... ]
END { FOR | LOOP } [ <label> ] ;

예를 들어 다음 FOR 루프는 5회 실행됩니다.

DECLARE
  counter INTEGER DEFAULT 0;
  maximum_count INTEGER default 5;
BEGIN
  FOR i IN 1 TO maximum_count DO
    counter := counter + 1;
  END FOR;
  RETURN counter;
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  counter INTEGER DEFAULT 0;
  maximum_count INTEGER default 5;
BEGIN
  FOR i IN 1 TO maximum_count DO
    counter := counter + 1;
  END FOR;
  RETURN counter;
END;
$$
;
+-----------------+
| anonymous block |
|-----------------|
|               5 |
+-----------------+

Snowflake Scripting 루프 내에 SQL 문을 포함할 수 있습니다. 예를 들어 다음 FOR 루프는 INSERT 문을 5회 실행하여 카운터 값을 테이블에 삽입합니다.

DECLARE
  counter INTEGER DEFAULT 0;
  maximum_count INTEGER default 5;
BEGIN
  CREATE OR REPLACE TABLE test_for_loop_insert(i INTEGER);
  FOR i IN 1 TO maximum_count DO
    INSERT INTO test_for_loop_insert VALUES (:i);
    counter := counter + 1;
  END FOR;
  RETURN counter || ' rows inserted';
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  counter INTEGER DEFAULT 0;
  maximum_count INTEGER default 5;
BEGIN
  CREATE OR REPLACE TABLE test_for_loop_insert(i INTEGER);
  FOR i IN 1 TO maximum_count DO
    INSERT INTO test_for_loop_insert VALUES (:i);
    counter := counter + 1;
  END FOR;
  RETURN counter || ' rows inserted';
END;
$$
;
+-----------------+
| anonymous block |
|-----------------|
| 5 rows inserted |
+-----------------+

테이블을 쿼리하여 삽입된 행을 봅니다.

SELECT * FROM test_for_loop_insert;
+---+
| I |
|---|
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+---+

다음 예제에서는 카운터 기반 FOR 루프를 사용하여 날짜 차원 테이블을 채우며, 이는 데이터 웨어하우스를 설정할 때 일반적인 작업입니다. 루프는 일 범위를 반복하고 특성이 계산된 각 날짜에 대한 행을 삽입합니다.

DECLARE
  start_date DATE DEFAULT '2025-01-01';
  current_date_val DATE;
BEGIN
  CREATE OR REPLACE TABLE date_dimension (
    date_key INTEGER,
    full_date DATE,
    day_of_week VARCHAR,
    month_name VARCHAR,
    quarter INTEGER,
    year INTEGER
  );
  FOR i IN 1 TO 7 DO
    current_date_val := DATEADD('day', :i - 1, :start_date);
    INSERT INTO date_dimension
      SELECT :i, :current_date_val, DAYNAME(:current_date_val),
        MONTHNAME(:current_date_val), QUARTER(:current_date_val),
        YEAR(:current_date_val);
  END FOR;
  RETURN 'Populated date dimension with 7 rows';
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  start_date DATE DEFAULT '2025-01-01';
  current_date_val DATE;
BEGIN
  CREATE OR REPLACE TABLE date_dimension (
    date_key INTEGER,
    full_date DATE,
    day_of_week VARCHAR,
    month_name VARCHAR,
    quarter INTEGER,
    year INTEGER
  );
  FOR i IN 1 TO 7 DO
    current_date_val := DATEADD('day', :i - 1, :start_date);
    INSERT INTO date_dimension
      SELECT :i, :current_date_val, DAYNAME(:current_date_val),
        MONTHNAME(:current_date_val), QUARTER(:current_date_val),
        YEAR(:current_date_val);
  END FOR;
  RETURN 'Populated date dimension with 7 rows';
END;
$$
;
+----------------------------------------+
| anonymous block                        |
|----------------------------------------|
| Populated date dimension with 7 rows   |
+----------------------------------------+

결과를 확인하려면 테이블을 쿼리합니다.

SELECT * FROM date_dimension ORDER BY date_key;
+----------+------------+-------------+------------+---------+------+
| DATE_KEY | FULL_DATE  | DAY_OF_WEEK | MONTH_NAME | QUARTER | YEAR |
|----------+------------+-------------+------------+---------+------|
|        1 | 2025-01-01 | Wed         | Jan        |       1 | 2025 |
|        2 | 2025-01-02 | Thu         | Jan        |       1 | 2025 |
|        3 | 2025-01-03 | Fri         | Jan        |       1 | 2025 |
|        4 | 2025-01-04 | Sat         | Jan        |       1 | 2025 |
|        5 | 2025-01-05 | Sun         | Jan        |       1 | 2025 |
|        6 | 2025-01-06 | Mon         | Jan        |       1 | 2025 |
|        7 | 2025-01-07 | Tue         | Jan        |       1 | 2025 |
+----------+------------+-------------+------------+---------+------+

FOR 루프에 대한 전체 구문 및 세부 사항은 FOR(Snowflake Scripting) 섹션을 참조하십시오.

커서 기반 FOR 루프

커서 기반 FOR 루프는 결과 세트를 반복합니다. 반복 횟수는 :doc:`커서 <cursors>`의 행 수에 따라 결정됩니다.

커서 기반 FOR 루프의 구문은 다음과 같습니다.

FOR <row_variable> IN <cursor_name> DO
  <statement>;
  [ <statement>; ... ]
END FOR [ <label> ] ;

이 섹션의 첫 번째 예제에서는 다음 invoices 테이블의 데이터를 사용합니다.

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

INSERT INTO invoices (price) VALUES
  (11.11),
  (22.22);

다음 예제에서는 FOR 루프를 사용하여 invoices 테이블에 대한 커서의 행을 반복합니다.

DECLARE
  total_price FLOAT;
  c1 CURSOR FOR SELECT price FROM invoices;
BEGIN
  total_price := 0.0;
  FOR record IN c1 DO
    total_price := total_price + record.price;
  END FOR;
  RETURN total_price;
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  total_price FLOAT;
  c1 CURSOR FOR SELECT price FROM invoices;
BEGIN
  total_price := 0.0;
  FOR record IN c1 DO
    total_price := total_price + record.price;
  END FOR;
  RETURN total_price;
END;
$$
;
+-----------------+
| anonymous block |
|-----------------|
|           33.33 |
+-----------------+

다음 예제에서는 커서 기반 FOR 루프를 사용하여 직원 테이블을 반복하고, 각 직원에게 부서에 따라 급여를 인상하고, 각 업데이트에 대한 감사 레코드를 삽입합니다.

CREATE OR REPLACE TABLE loop_test_employees (
  emp_id INTEGER,
  name VARCHAR,
  department VARCHAR,
  salary NUMBER(12,2));

INSERT INTO loop_test_employees VALUES
  (1, 'Alice', 'Engineering', 90000),
  (2, 'Bob', 'Sales', 70000),
  (3, 'Carol', 'Engineering', 95000),
  (4, 'Dave', 'Sales', 72000);

CREATE OR REPLACE TABLE salary_audit (
  emp_id INTEGER,
  old_salary NUMBER(12,2),
  new_salary NUMBER(12,2),
  updated_on TIMESTAMP);
DECLARE
  rows_updated INTEGER DEFAULT 0;
  raise_pct INTEGER;
  new_salary NUMBER(12,2);
  cur_emp_id INTEGER;
  cur_salary NUMBER(12,2);
  c1 CURSOR FOR SELECT emp_id, department, salary FROM loop_test_employees;
BEGIN
  FOR record IN c1 DO
    cur_emp_id := record.emp_id;
    cur_salary := record.salary;
    IF (record.department = 'Engineering') THEN
      raise_pct := 10;
    ELSE
      raise_pct := 5;
    END IF;
    new_salary := :cur_salary * (1 + :raise_pct / 100);
    UPDATE loop_test_employees SET salary = :new_salary WHERE emp_id = :cur_emp_id;
    INSERT INTO salary_audit
      SELECT :cur_emp_id, :cur_salary, :new_salary, CURRENT_TIMESTAMP();
    rows_updated := rows_updated + 1;
  END FOR;
  RETURN rows_updated || ' employees updated';
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  rows_updated INTEGER DEFAULT 0;
  raise_pct INTEGER;
  new_salary NUMBER(12,2);
  cur_emp_id INTEGER;
  cur_salary NUMBER(12,2);
  c1 CURSOR FOR SELECT emp_id, department, salary FROM loop_test_employees;
BEGIN
  FOR record IN c1 DO
    cur_emp_id := record.emp_id;
    cur_salary := record.salary;
    IF (record.department = 'Engineering') THEN
      raise_pct := 10;
    ELSE
      raise_pct := 5;
    END IF;
    new_salary := :cur_salary * (1 + :raise_pct / 100);
    UPDATE loop_test_employees SET salary = :new_salary WHERE emp_id = :cur_emp_id;
    INSERT INTO salary_audit
      SELECT :cur_emp_id, :cur_salary, :new_salary, CURRENT_TIMESTAMP();
    rows_updated := rows_updated + 1;
  END FOR;
  RETURN rows_updated || ' employees updated';
END;
$$
;
+----------------------+
| anonymous block      |
|----------------------|
| 4 employees updated  |
+----------------------+

업데이트를 확인하려면 테이블을 쿼리합니다.

SELECT emp_id, name, department, salary FROM loop_test_employees ORDER BY emp_id;
+--------+-------+-------------+-----------+
| EMP_ID | NAME  | DEPARTMENT  |    SALARY |
|--------+-------+-------------+-----------|
|      1 | Alice | Engineering |  99000.00 |
|      2 | Bob   | Sales       |  73500.00 |
|      3 | Carol | Engineering | 104500.00 |
|      4 | Dave  | Sales       |  75600.00 |
+--------+-------+-------------+-----------+
SELECT emp_id, old_salary, new_salary FROM salary_audit ORDER BY emp_id;
+--------+------------+------------+
| EMP_ID | OLD_SALARY | NEW_SALARY |
|--------+------------+------------|
|      1 |   90000.00 |   99000.00 |
|      2 |   70000.00 |   73500.00 |
|      3 |   95000.00 |  104500.00 |
|      4 |   72000.00 |   75600.00 |
+--------+------------+------------+

FOR 루프에 대한 전체 구문 및 세부 사항은 FOR(Snowflake Scripting) 섹션을 참조하십시오.

RESULTSET 기반 FOR 루프

RESULTSET 기반 FOR 루프는 결과 세트를 반복합니다. 반복 횟수는 RESULTSET 쿼리에 의해 반환된 행 수에 따라 결정됩니다.

RESULTSET 기반 FOR 루프의 구문은 다음과 같습니다.

FOR <row_variable> IN <RESULTSET_name> DO
  <statement>;
  [ <statement>; ... ]
END FOR [ <label> ] ;

이 섹션의 첫 번째 예제에서는 다음 invoices 테이블의 데이터를 사용합니다.

CREATE OR REPLACE TABLE invoices (price NUMBER(12, 2));
INSERT INTO invoices (price) VALUES
  (11.11),
  (22.22);

다음 예제에서는 FOR 루프를 사용하여 invoices 테이블의 RESULTSET에 있는 행을 반복합니다.

DECLARE
  total_price FLOAT;
  rs RESULTSET;
BEGIN
  total_price := 0.0;
  rs := (SELECT price FROM invoices);
  FOR record IN rs DO
    total_price := total_price + record.price;
  END FOR;
  RETURN total_price;
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  total_price FLOAT;
  rs RESULTSET;
BEGIN
  total_price := 0.0;
  rs := (SELECT price FROM invoices);
  FOR record IN rs DO
    total_price := total_price + record.price;
  END FOR;
  RETURN total_price;
END;
$$
;
+-----------------+
| anonymous block |
|-----------------|
|           33.33 |
+-----------------+

다음 예제에서는 RESULTSET 기반 FOR 루프를 사용하여 고객 레코드의 유효성을 검사합니다. 각 레코드에서 필요한 연락처 정보를 확인하고 status 열을 업데이트하여 각 레코드를 확인됨 또는 미완료로 표시합니다. 이러한 유형의 데이터 품질 검사는 추출, 변환 및 로드(ETL) 파이프라인의 일반적인 단계입니다.

CREATE OR REPLACE TABLE loop_test_customers (
  customer_id INTEGER,
  customer_name VARCHAR,
  customer_email VARCHAR,
  customer_phone VARCHAR,
  status VARCHAR DEFAULT 'pending_review');

INSERT INTO loop_test_customers (customer_id, customer_name, customer_email, customer_phone) VALUES
  (1, 'Alice Smith', 'alice@example.com', '800-555-0101'),
  (2, 'Bob Jones', NULL, '800-555-0102'),
  (3, 'Carol White', 'carol@example.com', NULL),
  (4, 'Dave Brown', NULL, NULL),
  (5, 'Eve Davis', 'eve@example.com', '800-555-0105');
DECLARE
  rs RESULTSET;
  valid_count INTEGER DEFAULT 0;
  invalid_count INTEGER DEFAULT 0;
  cur_customer_id INTEGER;
BEGIN
  rs := (SELECT customer_id, customer_email, customer_phone FROM loop_test_customers WHERE status = 'pending_review');
  FOR record IN rs DO
    cur_customer_id := record.customer_id;
    IF (record.customer_email IS NOT NULL AND record.customer_phone IS NOT NULL) THEN
      UPDATE loop_test_customers SET status = 'verified' WHERE customer_id = :cur_customer_id;
      valid_count := valid_count + 1;
    ELSE
      UPDATE loop_test_customers SET status = 'incomplete' WHERE customer_id = :cur_customer_id;
      invalid_count := invalid_count + 1;
    END IF;
  END FOR;
  RETURN 'Verified: ' || valid_count || ', Incomplete: ' || invalid_count;
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  rs RESULTSET;
  valid_count INTEGER DEFAULT 0;
  invalid_count INTEGER DEFAULT 0;
  cur_customer_id INTEGER;
BEGIN
  rs := (SELECT customer_id, customer_email, customer_phone FROM loop_test_customers WHERE status = 'pending_review');
  FOR record IN rs DO
    cur_customer_id := record.customer_id;
    IF (record.customer_email IS NOT NULL AND record.customer_phone IS NOT NULL) THEN
      UPDATE loop_test_customers SET status = 'verified' WHERE customer_id = :cur_customer_id;
      valid_count := valid_count + 1;
    ELSE
      UPDATE loop_test_customers SET status = 'incomplete' WHERE customer_id = :cur_customer_id;
      invalid_count := invalid_count + 1;
    END IF;
  END FOR;
  RETURN 'Verified: ' || valid_count || ', Incomplete: ' || invalid_count;
END;
$$
;
+-------------------------------+
| anonymous block               |
|-------------------------------|
| Verified: 2, Incomplete: 3   |
+-------------------------------+

변경 사항을 확인하려면 테이블을 쿼리합니다.

SELECT * FROM loop_test_customers ORDER BY customer_id;
+-------------+-----------------+-------------------+----------------+------------+
| CUSTOMER_ID | CUSTOMER_NAME   | CUSTOMER_EMAIL    | CUSTOMER_PHONE | STATUS     |
|-------------+-----------------+-------------------+----------------+------------|
|           1 | Alice Smith     | alice@example.com | 800-555-0101   | verified   |
|           2 | Bob Jones       | NULL              | 800-555-0102   | incomplete |
|           3 | Carol White     | carol@example.com | NULL           | incomplete |
|           4 | Dave Brown      | NULL              | NULL           | incomplete |
|           5 | Eve Davis       | eve@example.com   | 800-555-0105   | verified   |
+-------------+-----------------+-------------------+----------------+------------+

FOR 루프에 대한 전체 구문 및 세부 사항은 FOR(Snowflake Scripting) 섹션을 참조하십시오.

WHILE 루프

WHILE 루프는 조건이 true인 동안 반복됩니다. WHILE 루프에서 조건은 루프 본문을 실행하기 직전에 테스트됩니다. 첫 번째 반복 전에 조건이 false이면 루프 본문은 한 번도 실행되지 않습니다.

WHILE 루프의 구문은 다음과 같습니다.

WHILE ( <condition> ) { DO | LOOP }
  <statement>;
  [ <statement>; ... ]
END { WHILE | LOOP } [ <label> ] ;

예:

BEGIN
  LET counter := 0;
  WHILE (counter < 5) DO
    counter := counter + 1;
  END WHILE;
  RETURN counter;
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
BEGIN
  LET counter := 0;
  WHILE (counter < 5) DO
    counter := counter + 1;
  END WHILE;
  RETURN counter;
END;
$$
;
+-----------------+
| anonymous block |
|-----------------|
|               5 |
+-----------------+

다음 예제에서는 WHILE 루프를 사용하여 원시 트랜잭션 데이터에서 일일 판매 요약을 작성합니다. 한 번에 하나의 날짜를 처리하여 해당 날짜의 트랜잭션을 요약 행에 집계하고 로드됨으로 표시합니다. 이 날짜별 요약 패턴은 추출, 변환 및 로드(ETL) 파이프라인에서 일반적입니다.

CREATE OR REPLACE TABLE loop_test_raw_transactions (
  txn_id INTEGER,
  amount NUMBER(12,2),
  txn_date DATE,
  loaded BOOLEAN DEFAULT FALSE);

INSERT INTO loop_test_raw_transactions (txn_id, amount, txn_date) VALUES
  (1, 150.00, '2025-03-01'),
  (2, 230.50, '2025-03-01'),
  (3, 89.99, '2025-03-01'),
  (4, 412.00, '2025-03-02'),
  (5, 55.25, '2025-03-03'),
  (6, 178.75, '2025-03-03');

CREATE OR REPLACE TABLE loop_test_daily_sales_summary (
  summary_date DATE,
  total_sales NUMBER(12,2),
  txn_count INTEGER);
DECLARE
  next_date DATE;
BEGIN
  next_date := (SELECT MIN(txn_date) FROM loop_test_raw_transactions WHERE NOT loaded);
  WHILE (next_date IS NOT NULL) DO
    INSERT INTO loop_test_daily_sales_summary
      SELECT txn_date, SUM(amount), COUNT(*)
      FROM loop_test_raw_transactions
      WHERE txn_date = :next_date AND NOT loaded
      GROUP BY txn_date;
    UPDATE loop_test_raw_transactions SET loaded = TRUE WHERE txn_date = :next_date;
    next_date := (SELECT MIN(txn_date) FROM loop_test_raw_transactions WHERE NOT loaded);
  END WHILE;
  RETURN 'Daily summaries created for all transaction dates';
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  next_date DATE;
BEGIN
  next_date := (SELECT MIN(txn_date) FROM loop_test_raw_transactions WHERE NOT loaded);
  WHILE (next_date IS NOT NULL) DO
    INSERT INTO loop_test_daily_sales_summary
      SELECT txn_date, SUM(amount), COUNT(*)
      FROM loop_test_raw_transactions
      WHERE txn_date = :next_date AND NOT loaded
      GROUP BY txn_date;
    UPDATE loop_test_raw_transactions SET loaded = TRUE WHERE txn_date = :next_date;
    next_date := (SELECT MIN(txn_date) FROM loop_test_raw_transactions WHERE NOT loaded);
  END WHILE;
  RETURN 'Daily summaries created for all transaction dates';
END;
$$
;
+----------------------------------------------------+
| anonymous block                                    |
|----------------------------------------------------|
| Daily summaries created for all transaction dates  |
+----------------------------------------------------+

결과를 확인하려면 요약 테이블을 쿼리합니다.

SELECT * FROM loop_test_daily_sales_summary ORDER BY summary_date;
+--------------+-------------+-----------+
| SUMMARY_DATE | TOTAL_SALES | TXN_COUNT |
|--------------+-------------+-----------|
| 2025-03-01   |      470.49 |         3 |
| 2025-03-02   |      412.00 |         1 |
| 2025-03-03   |      234.00 |         2 |
+--------------+-------------+-----------+

WHILE 루프에 대한 전체 구문 및 세부 사항은 WHILE(Snowflake Scripting) 섹션을 참조하십시오.

REPEAT 루프

REPEAT 루프는 조건이 true일 때까지 반복됩니다. REPEAT 루프에서 조건은 루프 본문을 실행한 직후에 테스트됩니다. 결과적으로, 루프의 본문은 항상 적어도 한 번 실행됩니다.

REPEAT 루프의 구문은 다음과 같습니다.

REPEAT
  <statement>;
  [ <statement>; ... ]
UNTIL ( <condition> )
END REPEAT [ <label> ] ;

예:

BEGIN
  LET counter := 5;
  LET number_of_iterations := 0;
  REPEAT
    counter := counter - 1;
    number_of_iterations := number_of_iterations + 1;
  UNTIL (counter = 0)
  END REPEAT;
  RETURN number_of_iterations;
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
BEGIN
  LET counter := 5;
  LET number_of_iterations := 0;
  REPEAT
    counter := counter - 1;
    number_of_iterations := number_of_iterations + 1;
  UNTIL (counter = 0)
  END REPEAT;
  RETURN number_of_iterations;
END;
$$
;
+-----------------+
| anonymous block |
|-----------------|
|               5 |
+-----------------+

다음 예제에서는 추가 batch_id 열로 스테이징 테이블과 대상 테이블을 생성합니다. 그런 다음 REPEAT 루프를 사용하여 스테이징 테이블에서 대상 테이블로 행을 일괄적으로 이동하고 각 반복 후 스테이징 테이블에 행이 남아 있지 않을 때까지 일괄 ID를 증분합니다.

CREATE OR REPLACE TABLE loop_test_orders_staging (
  order_id INTEGER,
  customer VARCHAR,
  amount NUMBER(12,2));

INSERT INTO loop_test_orders_staging VALUES
  (101, 'TestA Corp', 500.00),
  (102, 'TestB Corp', 1200.00),
  (103, 'TestA Corp', 300.00),
  (104, 'TestC Corp', 750.00),
  (105, 'TestB Corp', 425.00),
  (106, 'TestC Corp', 980.00);

CREATE OR REPLACE TABLE loop_test_orders_processed (
    order_id INTEGER,
    customer VARCHAR,
    amount NUMBER(12,2),
    batch_id INTEGER);
DECLARE
  batch_size INTEGER DEFAULT 2;
  batch_id INTEGER DEFAULT 1;
  remaining INTEGER;
BEGIN
  remaining := (SELECT COUNT(*) FROM loop_test_orders_staging);
  REPEAT
    INSERT INTO loop_test_orders_processed
      SELECT order_id, customer, amount, :batch_id
      FROM loop_test_orders_staging
      ORDER BY order_id
      LIMIT :batch_size;
    DELETE FROM loop_test_orders_staging WHERE order_id IN (
      SELECT order_id
        FROM loop_test_orders_processed
        WHERE batch_id = :batch_id
    );
    batch_id := batch_id + 1;
    remaining := (SELECT COUNT(*) FROM loop_test_orders_staging);
  UNTIL (remaining = 0)
  END REPEAT;
  RETURN 'Processed all orders in ' || (batch_id - 1) || ' batches';
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  batch_size INTEGER DEFAULT 2;
  batch_id INTEGER DEFAULT 1;
  remaining INTEGER;
BEGIN
  remaining := (SELECT COUNT(*) FROM loop_test_orders_staging);
  REPEAT
    INSERT INTO loop_test_orders_processed
      SELECT order_id, customer, amount, :batch_id
      FROM loop_test_orders_staging
      ORDER BY order_id
      LIMIT :batch_size;
    DELETE FROM loop_test_orders_staging WHERE order_id IN (
      SELECT order_id
        FROM loop_test_orders_processed
        WHERE batch_id = :batch_id
    );
    batch_id := batch_id + 1;
    remaining := (SELECT COUNT(*) FROM loop_test_orders_staging);
  UNTIL (remaining = 0)
  END REPEAT;
  RETURN 'Processed all orders in ' || (batch_id - 1) || ' batches';
END;
$$
;
+------------------------------------+
| anonymous block                    |
|------------------------------------|
| Processed all orders in 3 batches  |
+------------------------------------+

결과를 확인하려면 대상 테이블을 쿼리합니다.

SELECT * FROM loop_test_orders_processed ORDER BY batch_id, order_id;
+----------+------------+---------+----------+
| ORDER_ID | CUSTOMER   |  AMOUNT | BATCH_ID |
|----------+------------+---------+----------|
|      101 | TestA Corp |  500.00 |        1 |
|      102 | TestB Corp | 1200.00 |        1 |
|      103 | TestA Corp |  300.00 |        2 |
|      104 | TestC Corp |  750.00 |        2 |
|      105 | TestB Corp |  425.00 |        3 |
|      106 | TestC Corp |  980.00 |        3 |
+----------+------------+---------+----------+

REPEAT 루프에 대한 전체 구문 및 세부 사항은 REPEAT(Snowflake Scripting) 섹션을 참조하십시오.

LOOP 루프

LOOP 루프는 BREAK 명령이 실행될 때까지 실행됩니다. BREAK 명령은 일반적으로 분기 논리(예: IF 문 문 또는 CASE 문 문) 내부에 포함됩니다.

LOOP 문의 구문은 다음과 같습니다.

LOOP
  <statement>;
  [ <statement>; ... ]
END LOOP [ <label> ] ;

예:

BEGIN
  LET counter := 5;
  LOOP
    IF (counter = 0) THEN
      BREAK;
    END IF;
    counter := counter - 1;
  END LOOP;
  RETURN counter;
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
BEGIN
  LET counter := 5;
  LOOP
    IF (counter = 0) THEN
      BREAK;
    END IF;
    counter := counter - 1;
  END LOOP;
  RETURN counter;
END;
$$
;
+-----------------+
| anonymous block |
|-----------------|
|               0 |
+-----------------+

다음 예제에서는 여러 날짜에 걸친 항목이 있는 로그 테이블을 생성합니다. LOOP를 사용하여 마감 날짜보다 오래된 행을 아카이브 테이블에 보관하고 소스에서 삭제하며, 적격 행이 더 이상 남아 있지 않을 때까지 한 번에 하루씩 처리합니다.

CREATE OR REPLACE TABLE loop_test_event_log (
  event_id INTEGER,
  event_date DATE,
  event_description VARCHAR);

INSERT INTO loop_test_event_log VALUES
  (1, DATEADD('month', -3, CURRENT_DATE()), 'User login'),
  (2, DATEADD('month', -3, CURRENT_DATE()), 'File upload'),
  (3, DATEADD('month', -2, CURRENT_DATE()), 'Password change'),
  (4, DATEADD('month', -1, CURRENT_DATE()), 'User login'),
  (5, DATEADD('month', -1, CURRENT_DATE()), 'Data export');

CREATE OR REPLACE TABLE loop_test_event_log_archive (
  event_id INTEGER,
  event_date DATE,
  event_description VARCHAR,
  archived_on DATE);
DECLARE
  cutoff_date DATE DEFAULT DATEADD('month', -1, CURRENT_DATE());
  oldest_date DATE;
  archived_total INTEGER DEFAULT 0;
  batch_count INTEGER;
BEGIN
  LOOP
    oldest_date := (SELECT MIN(event_date)
                      FROM loop_test_event_log
                      WHERE event_date < :cutoff_date);
    IF (oldest_date IS NULL) THEN
      BREAK;
    END IF;
    batch_count := (SELECT COUNT(*)
                      FROM loop_test_event_log
                      WHERE event_date = :oldest_date);
    INSERT INTO loop_test_event_log_archive
      SELECT event_id, event_date, event_description, CURRENT_DATE()
        FROM loop_test_event_log
        WHERE event_date = :oldest_date;
    DELETE FROM loop_test_event_log WHERE event_date = :oldest_date;
    archived_total := archived_total + batch_count;
  END LOOP;
  RETURN 'Archived ' || archived_total || ' events';
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
DECLARE
  cutoff_date DATE DEFAULT DATEADD('month', -1, CURRENT_DATE());
  oldest_date DATE;
  archived_total INTEGER DEFAULT 0;
  batch_count INTEGER;
BEGIN
  LOOP
    oldest_date := (SELECT MIN(event_date)
                      FROM loop_test_event_log
                      WHERE event_date < :cutoff_date);
    IF (oldest_date IS NULL) THEN
      BREAK;
    END IF;
    batch_count := (SELECT COUNT(*)
                      FROM loop_test_event_log
                      WHERE event_date = :oldest_date);
    INSERT INTO loop_test_event_log_archive
      SELECT event_id, event_date, event_description, CURRENT_DATE()
        FROM loop_test_event_log
        WHERE event_date = :oldest_date;
    DELETE FROM loop_test_event_log WHERE event_date = :oldest_date;
    archived_total := archived_total + batch_count;
  END LOOP;
  RETURN 'Archived ' || archived_total || ' events';
END;
$$
;
+---------------------+
| anonymous block     |
|---------------------|
| Archived 3 events   |
+---------------------+

결과를 확인하려면 두 테이블을 모두 쿼리합니다.

SELECT * FROM loop_test_event_log ORDER BY event_id;
+----------+------------+-------------------+
| EVENT_ID | EVENT_DATE | EVENT_DESCRIPTION |
|----------+------------+-------------------|
|        4 | 2026-02-04 | User login        |
|        5 | 2026-02-04 | Data export       |
+----------+------------+-------------------+
SELECT event_id,
       event_date,
       event_description
  FROM loop_test_event_log_archive
  ORDER BY event_id;
+----------+------------+-------------------+
| EVENT_ID | EVENT_DATE | EVENT_DESCRIPTION |
|----------+------------+-------------------|
|        1 | 2025-12-04 | User login        |
|        2 | 2025-12-04 | File upload       |
|        3 | 2026-01-04 | Password change   |
+----------+------------+-------------------+

LOOP 루프에 대한 전체 구문 및 세부 사항은 LOOP(Snowflake Scripting) 섹션을 참조하십시오.

루프 또는 반복 종료하기

루프 구조체에서 루프 또는 루프의 반복이 일찍 종료되어야 하는 시기를 지정할 수 있습니다. 다음 섹션에서는 이에 대해 자세히 설명합니다.

루프 종료하기

BREAK 명령을 실행하여 루프를 명시적으로 조기에 종료할 수 있습니다. BREAK(및 동의어 EXIT)는 현재 반복을 즉시 중지하고 나머지 반복을 건너뜁니다. BREAK 는 루프가 끝난 후 첫 번째 실행 가능 문으로 이동하는 것으로 생각할 수 있습니다.

BREAK는 LOOP 루프에 필요하지만, WHILE, FOR 및 REPEAT 루프에는 필요하지 않습니다. 대부분의 경우, 건너뛰고 싶은 문이 있는 경우 표준 분기 구문(IF 문CASE 문)을 사용하여, 루프 내에서 실행할 문을 제어할 수 있습니다.

BREAK 명령 자체는 일반적으로 IF 또는 CASE 문 내부에 있습니다.

루프를 종료하지 않고 반복 종료하기

CONTINUE (또는 ITERATE) 명령을 사용하여 루프의 나머지 문을 건너뛰고 루프 반복의 끝으로 이동할 수 있습니다 루프는 다음 반복이 시작될 때 계속됩니다.

이러한 이동은 거의 필요하지 않습니다. 대부분의 경우, 건너뛰고 싶은 문이 있는 경우 표준 분기 구문(IF 문CASE 문)을 사용하여, 루프 내에서 실행할 문을 제어할 수 있습니다.

CONTINUE 또는 ITERATE 명령 자체는 일반적으로 IF 또는 CASE 문 내부에 있습니다.

종료 후 실행을 계속해야 하는 위치 지정하기

BREAK 또는 CONTINUE 명령에서 코드의 특정 지점(예: 중첩 루프의 외부 루프)에서 실행을 계속해야 하는 경우, 실행을 계속해야 하는 지점을 식별하는 레이블을 지정합니다.

다음 예에서는 중첩 루프에서 이를 보여줍니다.

BEGIN
  LET inner_counter := 0;
  LET outer_counter := 0;
  LOOP
    LOOP
      IF (inner_counter < 5) THEN
        inner_counter := inner_counter + 1;
        CONTINUE OUTER;
      ELSE
        BREAK OUTER;
      END IF;
    END LOOP INNER;
    outer_counter := outer_counter + 1;
    BREAK;
  END LOOP OUTER;
  RETURN ARRAY_CONSTRUCT(outer_counter, inner_counter);
END;

참고: Python Connector 코드에서 Snowflake CLI, SnowSQL, Classic Console, execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하세요(Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector 참조).

EXECUTE IMMEDIATE $$
BEGIN
  LET inner_counter := 0;
  LET outer_counter := 0;
  LOOP
    LOOP
      IF (inner_counter < 5) THEN
        inner_counter := inner_counter + 1;
        CONTINUE OUTER;
      ELSE
        BREAK OUTER;
      END IF;
    END LOOP INNER;
    outer_counter := outer_counter + 1;
    BREAK;
  END LOOP OUTER;
  RETURN ARRAY_CONSTRUCT(outer_counter, inner_counter);
END;
$$;

이 예제에서:

  • OUTER 라는 레이블의 루프에 중첩된 INNER 라는 레이블의 루프가 있습니다.

  • CONTINUE OUTER는 OUTER 레이블이 지정된 루프의 또 다른 반복을 시작합니다.

  • BREAK OUTER는 내부 루프를 종료하고 외부 루프(OUTER로 레이블이 지정됨)의 끝으로 제어를 전송합니다.

이 명령의 출력은 다음과 같습니다.

+-----------------+
| anonymous block |
|-----------------|
| [               |
|   0,            |
|   5             |
| ]               |
+-----------------+

출력에 표시된 대로:

  • inner_counter 는 5까지 증가합니다. CONTINUE OUTER는 외부 루프의 새 반복을 시작하고, 이는 내부 루프의 새 반복을 시작하여 이 카운터를 최대 5까지 증가시킵니다. 이러한 반복은 inner_counter 의 값이 5가 되고 BREAK OUTER가 내부 루프를 종료할 때까지 계속됩니다.

  • outer_counter 는 절대 증가하지 않습니다. 이 카운터를 증가시키는 문에는 절대 도달하지 않습니다. BREAK OUTER가 외부 루프의 끝으로 제어를 전송하기 때문입니다.