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 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 语句,以将计数器的值插入表中:
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 循环
当条件为 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 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;
$$;
在此示例中:
有一个标记为 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 将控制权转移到外循环的末尾,因此从不会到达让此计数器递增的语句。