使用循环

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 循环执行五次:

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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 语句,以将计数器的值插入表中:

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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 循环

当条件为 true WHILE 循环进行迭代。在 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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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 CLISnowSQL、Classic Console,或者 execute_streamexecute_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;
$$;

在此示例中:

  • 有一个标记为 INNER 的循环,它嵌套在标记为 OUTER 的循环中。

  • 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 将控制权转移到外循环的末尾,因此从不会到达让此计数器递增的语句。