简单循环处理I
此写法是先取一条数据,然后判断循环条件
-- 清空日志表. Test=# truncate table log_table; TRUNCATE TABLE Test=# select * from test_main; id | value ----+------- 1 | ONE 2 | TWO 4 | FOUR (3 行记录) CREATE OR REPLACE FUNCTION TestCursor() RETURNS void AS $$ DECLARE -- 定义游标. c_test_main CURSOR FOR SELECT id, value FROM test_main; -- 定义存储数据的变量. v_id INT; v_value VARCHAR(10); BEGIN -- 打开游标. OPEN c_test_main; -- 填充数据. FETCH c_test_main INTO v_id, v_value; -- 假如检索到了数据,才处理. while found loop INSERT INTO log_table VALUES (v_value); -- 填充下一条数据. FETCH c_test_main INTO v_id, v_value; end loop; -- 关闭游标 CLOSE c_test_main; END; $$ LANGUAGE plpgsql; Test=# select TestCursor(); testcursor ------------ (1 行记录) Test=# select * from log_table; log_text ---------- ONE TWO FOUR (3 行记录)
如果 SQL 语句中, 列的数量很多, 那么定义很多的变量,对应没一列。 开发、维护成本都会比较高。 这种情况下, 可以采用 表名%rowtype 的方式, 只定义一个变量来进行处理. -- 清空日志表. Test=# truncate table log_table; TRUNCATE TABLE CREATE OR REPLACE FUNCTION TestCursor() RETURNS void AS $$ DECLARE -- 定义游标. c_test_main CURSOR FOR SELECT id, value FROM test_main ORDER BY id DESC; -- 定义存储数据的变量. v_main_data test_main%ROWTYPE; BEGIN -- 打开游标. OPEN c_test_main; -- 填充数据. FETCH c_test_main INTO v_main_data; -- 假如检索到了数据,才处理. while found loop INSERT INTO log_table VALUES (v_main_data.value); -- 填充下一条数据. FETCH c_test_main INTO v_main_data; end loop; -- 关闭游标 CLOSE c_test_main; END; $$ LANGUAGE plpgsql; Test=# select TestCursor(); testcursor ------------ (1 行记录) Test=# select * from log_table; log_text ---------- FOUR TWO ONE (3 行记录)
简单循环处理II
此写法是先开始循环,然后取数据,然后判断条件
-- 清空日志表. Test=# truncate table log_table; TRUNCATE TABLE Test=# select * from test_main; id | value ----+------- 1 | ONE 2 | TWO 4 | FOUR (3 行记录) CREATE OR REPLACE FUNCTION TestCursor2() RETURNS void AS $$ DECLARE -- 定义游标. c_test_main CURSOR FOR SELECT id, value FROM test_main; -- 定义存储数据的变量. v_id INT; v_value VARCHAR(10); BEGIN -- 打开游标. OPEN c_test_main; LOOP -- 填充数据. FETCH c_test_main INTO v_id, v_value; -- 假如没有检索到数据,结束循环处理 Exit when NOT found; -- 游标每一行的实际操作. INSERT INTO log_table VALUES (v_value); END LOOP; -- 关闭游标 CLOSE c_test_main; END; $$ LANGUAGE plpgsql; Test=# select TestCursor2(); testcursor2 ------------- (1 行记录) Test=# select * from log_table; log_text ---------- ONE TWO FOUR (3 行记录)
支持来回滚动的游标
-- 清空日志表. Test=# truncate table log_table; TRUNCATE TABLE -- 为达到测试效果, 多加几行测试数据. INSERT INTO test_main(id, value) VALUES (11, 'ONEONE'); INSERT INTO test_main(id, value) VALUES (12, 'ONETWO'); INSERT INTO test_main(id, value) VALUES (13, 'ONETHREE'); Test=# SELECT id, value FROM test_main ORDER BY id; id | value ----+---------- 1 | ONE1 2 | TWO1 4 | FOUR1 11 | ONEONE 12 | ONETWO 13 | ONETHREE (6 行记录) CREATE OR REPLACE FUNCTION TestCursorScroll() RETURNS void AS $$ DECLARE -- 定义游标. c_test_main SCROLL CURSOR FOR SELECT id, value FROM test_main ORDER BY id; -- 定义存储数据的变量. v_main_data test_main%ROWTYPE; BEGIN -- 打开游标. OPEN c_test_main; -- 填充数据. FETCH FIRST FROM c_test_main INTO v_main_data; INSERT INTO log_table VALUES ('游标第一行:' || v_main_data.value); -- 填充数据. FETCH LAST FROM c_test_main INTO v_main_data; INSERT INTO log_table VALUES ('游标最末行:' || v_main_data.value); -- 填充数据. FETCH ABSOLUTE 3 FROM c_test_main INTO v_main_data; INSERT INTO log_table VALUES ('游标绝对第3行:' || v_main_data.value); -- 填充数据. FETCH RELATIVE 2 FROM c_test_main INTO v_main_data; INSERT INTO log_table VALUES ('游标相对第2行:' || v_main_data.value); -- 填充数据. FETCH PRIOR FROM c_test_main INTO v_main_data; INSERT INTO log_table VALUES ('游标上一行:' || v_main_data.value); -- 填充数据. FETCH NEXT FROM c_test_main INTO v_main_data; INSERT INTO log_table VALUES ('游标下一行:' || v_main_data.value); -- 关闭游标 CLOSE c_test_main; END; $$ LANGUAGE plpgsql; Test=# select TestCursorScroll() ; testcursorscroll ------------------ (1 行记录) Test=# select * from log_table; log_text ---------------------- 游标第一行:ONE1 游标最末行:ONETHREE 游标绝对第3行:FOUR1 游标相对第2行:ONETWO 游标上一行:ONEONE 游标下一行:ONETWO (6 行记录)
带参数的游标
这里同时需要使用前面外键约束的 test_sub 表
-- 清空日志表. Test=# truncate table log_table; TRUNCATE TABLE CREATE OR REPLACE FUNCTION TestCursorMainSub() RETURNS void AS $$ DECLARE -- 定义游标(主表). c_test_main CURSOR FOR SELECT id, value FROM test_main; -- 保存主表游标数据的变量 v_main_data test_main%ROWTYPE; -- 定义游标(子表). c_test_sub CURSOR(p_main_id INT) IS SELECT id, main_id, value FROM test_sub WHERE main_id = p_main_id; -- 保存子表游标数据的变量 v_sub_data test_sub%ROWTYPE; BEGIN -- 打开游标(主表). OPEN c_test_main; -- 开始循环处理(主表). LOOP -- 填充数据(主表). FETCH c_test_main INTO v_main_data; -- 假如没有检索到(主表)数据,结束循环处理 Exit when NOT found; -- 主表每一行的实际操作. INSERT INTO log_table VALUES ( 'Main:' || v_main_data.value ); -- 打开游标(子表). OPEN c_test_sub(v_main_data.id); -- 开始循环处理(子表). LOOP -- 填充数据(子表). FETCH c_test_sub INTO v_sub_data; -- 假如没有检索到(主表)数据,结束循环处理 Exit when NOT found; -- 子表每一行的实际操作. INSERT INTO log_table VALUES ( '-->Sub:' || v_sub_data.value ); END LOOP; -- 关闭游标(子表). CLOSE c_test_sub; END LOOP; -- 关闭游标(主表). CLOSE c_test_main; END; $$ LANGUAGE plpgsql; Test=# select TestCursorMainSub(); testcursormainsub ------------------- (1 行记录) Test=# select * from log_table; log_text ----------------- Main:ONE -->Sub:ONEONE Main:TWO -->Sub:TWOTWO Main:FOUR -->Sub:FOURFOUR (6 行记录)
用于更新的游标
CREATE OR REPLACE FUNCTION TestUpdateCursor() RETURNS void AS $$ DECLARE -- 定义游标. c_test_main CURSOR FOR SELECT id, value FROM test_main FOR UPDATE ; -- 定义存储数据的变量. v_main_data test_main%ROWTYPE; BEGIN -- 打开游标. OPEN c_test_main; LOOP -- 填充数据. FETCH c_test_main INTO v_main_data; -- 假如没有检索到数据,结束循环处理 Exit when NOT found; -- 游标每一行的实际操作. -- 更新数据. UPDATE test_main SET value = value || '1' WHERE CURRENT OF c_test_main; END LOOP; -- 关闭游标 CLOSE c_test_main; END; $$ LANGUAGE plpgsql; Test=# select TestUpdateCursor(); testupdatecursor ------------------ (1 行记录) Test=# select * from test_main; id | value ----+------- 1 | ONE1 2 | TWO1 4 | FOUR1 (3 行记录)
FOR的使用
-- 清空日志表. Test=# truncate table log_table; TRUNCATE TABLE CREATE OR REPLACE FUNCTION TestCursorFor() RETURNS void AS $$ DECLARE -- 定义存储数据的变量. v_main_data RECORD; BEGIN FOR v_main_data IN SELECT id, value FROM test_main LOOP -- 每一行的实际操作. INSERT INTO log_table VALUES (v_main_data.value); END LOOP; END; $$ LANGUAGE plpgsql; Test=# SELECT TestCursorFor(); testcursorfor --------------- (1 行记录) Test=# select * from log_table; log_text ---------- ONE1 TWO1 FOUR1 (3 行记录)