ループの操作

Snowflakeスクリプトは、次のタイプのループをサポートしています。

このトピックでは、こうした各タイプのループの使用方法について説明します。

FOR ループ

FOR ループは、指定された回数、または結果セットの各行に対して一連のステップを繰り返します。Snowflakeスクリプトは、次のタイプの 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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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スクリプト) をご参照ください。

カーソルベースの FOR ループ

カーソルベースのFORループは、結果セットを反復処理します。反復回数は、 カーソル の行数によって決まります。

カーソルベースの 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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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スクリプト) をご参照ください。

RESULTSET ベースの FOR ループ

RESULTSETベースのFORループは、結果セットを反復処理します。反復回数は、:doc:`RESULTSET<resultsets>`クエリが返す行数によって決まります。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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スクリプト) をご参照ください。

WHILE ループ

WHILE ループは、条件がtrueの 、反復されます。WHILE ループでは、ループの本文を実行する直前に条件がテストされます。最初の反復の前に条件がfalseの場合、ループの本文は1回も実行されません。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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

次の例では、WHILEループを使用して、未加工のトランザクションデータから日次売上の概要を構築します。一度に1つの日付を処理し、その日付のトランザクションを要約行に集計して、ロード済みとしてマークします。このような日付ごとの要約パターンは、抽出、変換、ロード(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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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スクリプト) をご参照ください。

REPEAT ループ

REPEAT ループは、条件がtrueに なるまで 反復されます。REPEAT ループでは、ループの本文を実行した直後に条件がテストされます。その結果、ループの本文は常に少なくとも1回実行されます。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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スクリプト) をご参照ください。

LOOP ループ

BREAK コマンドが実行されるまで、 LOOP ループは実行されます。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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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を使用して、カットオフ日より古い行をアーカイブテーブルにアーカイブしてソースから削除し、対象となる行がなくなるまで1日ずつ処理します。

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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スクリプト) をご参照ください。

ループまたは反復の終了

ループ構造では、ループまたはループの反復を早期に終了するときを指定できます。次のセクションでは、これについて詳しく説明します。

ループの終了

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;

注意:Snowflake CLISnowSQL、クラシックコンソール、または:code:execute_stream`や:code:`execute_string`メソッドを:doc:`Pythonコネクタ</developer-guide/python-connector/python-connector>`コードで使用する場合は、代わりに次の例を使用してください(:doc:/developer-guide/snowflake-scripting/running-examples`を参照)。

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 が制御を外側のループの最後に移すため、このカウンターに到達することはありません。