GaussDB SQL基础语法示例:循环语句详解
循环是数据库编程中实现迭代操作的核心工具,GaussDB基于PL/pgSQL支持多种循环结构。本文通过真实业务场景,系统讲解循环的语法形式、应用场景及优化技巧。
一、基础循环结构
- LOOP基础循环
DO $$
DECLARE
counter INT := 0;
BEGIN
LOOP
counter := counter + 1;
RAISE NOTICE '当前值:%', counter;
EXIT WHEN counter >= 5; -- 执行5次后退出
END LOOP;
END
$$;
- WHILE条件循环
DO $$
DECLARE
i INT := 10;
BEGIN
WHILE i > 0 LOOP
RAISE NOTICE '倒计时:%', i;
i := i - 1;
END LOOP;
END
$$;
- FOR范围循环
DO $$
BEGIN
FOR num IN 1..5 LOOP
RAISE NOTICE '循环计数:%', num;
END LOOP;
-- 反向循环
FOR num IN REVERSE 1..5 LOOP
RAISE NOTICE '逆序计数:%', num;
END LOOP;
END
$$;
二、游标循环处理
- 显式游标循环
DO $$
DECLARE
cur SYS_REFCURSOR;
emp_id INT;
emp_name VARCHAR;
BEGIN
OPEN cur FOR SELECT id, name FROM employees;
LOOP
FETCH cur INTO emp_id, emp_name;
EXIT WHEN cur%NOTFOUND;
RAISE NOTICE '员工:%,ID:%', emp_name, emp_id;
END LOOP;
CLOSE cur;
END
$$;
- FOR游标自动循环
DO $$
DECLARE
cur REFCURSOR;
dept_record RECORD;
BEGIN
cur := 'SELECT * FROM departments'::REFCURSOR;
LOOP
FETCH cur INTO dept_record;
EXIT WHEN cur%NOTFOUND;
RAISE NOTICE '部门:% (人数:%)',
dept_record.dept_name,
dept_record.employee_count;
END LOOP;
END
$$;
三、高级应用场景
- 批量数据操作
CREATE OR REPLACE PROCEDURE batch_update_salary()
LANGUAGE plpgsql AS $$
DECLARE
emp_rec RECORD;
BEGIN
FOR emp_rec IN
SELECT id, salary FROM employees WHERE dept = 'IT'
LOOP
UPDATE employees
SET salary = salary * 1.1
WHERE CURRENT OF emp_rec; -- 使用游标当前记录
END LOOP;
END;
$$;
-- 调用存储过程
CALL batch_update_salary();
- 动态SQL循环
DO $$
DECLARE
sql_query TEXT;
table_name TEXT := 'sales';
BEGIN
sql_query := format('SELECT * FROM %I', table_name);
EXECUTE sql_query;
-- 循环处理动态结果
FOR i IN 1..10 LOOP
RAISE NOTICE '动态循环执行第%次', i;
END LOOP;
END
$$;
四、性能优化技巧
- 批量处理代替逐行循环
-- 优化前(逐行处理)
DO $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN SELECT * FROM large_table LOOP
INSERT INTO temp_table VALUES (rec.*);
END LOOP;
END
$$;
-- 优化后(批量操作)
INSERT INTO temp_table
SELECT * FROM large_table; -- 单次操作提升性能
- 游标缓存优化
-- 使用WITH HOLD游标保持结果集
BEGIN
DECLARE cur SCROLL CURSOR WITH HOLD FOR
SELECT * FROM orders;
-- 后续循环处理...
END;
五、关键注意事项
循环控制
-- 错误示例:缺少EXIT导致无限循环
DO $$
DECLARE
i INT := 0;
BEGIN
LOOP
i := i + 1;
RAISE NOTICE '值:%', i;
-- 缺少EXIT条件将永久循环
END LOOP;
END
```2. **作用域管理**```sql -- 错误示例:嵌套块变量覆盖 DO $$ DECLARE counter INT := 10; BEGIN DO
DECLARE
counter INT := 20; -- 内层变量遮蔽外层
BEGIN
RAISE NOTICE '内层值:%', counter;
END $$;
RAISE NOTICE '外层值:%', counter; -- 仍输出10
END $$;
- 资源释放
-- 必须显式关闭游标
DO $$
DECLARE
cur REFCURSOR;
BEGIN
OPEN cur FOR SELECT * FROM employees;
-- ...处理数据...
CLOSE cur; -- 防止资源泄漏
END
$$;
六、典型错误案例
-- 错误1:未处理NULL值
DO $$
DECLARE
result INT;
BEGIN
SELECT salary INTO result FROM employees WHERE id=999; -- 无记录返回
RAISE NOTICE '薪资:%', result; -- 输出NULL
END
$$;
-- 正确做法:添加NULL判断
DO $$
DECLARE
result INT;
BEGIN
SELECT salary INTO result FROM employees WHERE id=999;
IF FOUND THEN
RAISE NOTICE '薪资:%', result;
ELSE
RAISE NOTICE '记录不存在';
END IF;
END
$$;
-- 错误2:类型不匹配
DO $$
DECLARE
num_str TEXT := '123A';
BEGIN
num_str := num_str::INT; -- 转换失败抛出异常
END
$$;
-- 正确写法:使用安全转换
DO $$
DECLARE
num_str TEXT := '123A';
num_num INT;
BEGIN
BEGIN
num_num := num_str::INT;
EXCEPTION WHEN others THEN
num_num := 0;
END;
END
$$;
七、实战技巧
分页查询实现
DO $$
DECLARE
page_size INT := 100;
current_page INT := 3;
offset_value INT;
BEGIN
offset_value := (current_page-1)*page_size;
FOR rec IN
SELECT * FROM large_table
ORDER BY id
LIMIT page_size OFFSET offset_value
LOOP
-- 处理每页数据
END LOOP;
END
$$;
树形结构遍历
WITH RECURSIVE category_tree AS (
SELECT id, parent_id, name
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.parent_id, c.name
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree;
通过合理运用循环语句,可以实现复杂的数据处理逻辑。建议结合GaussDB的EXPLAIN ANALYZE工具分析循环性能,对大批量数据处理优先考虑基于集合的操作替代逐行循环。更多高级用法可参考《GaussDB PL/pgSQL编程指南》。