PostgreSQL教程FG169-PL/pgSQL实战:业务逻辑封装为存储过程
本文档风哥主要介绍PostgreSQL数据库PL/pgSQL存储过程实战应用,包括业务逻辑封装方法、存储过程创建与调用、事务处理、异常处理、批量数据处理等内容,风哥教程参考PostgreSQL官方文档Server Programming、Procedural Languages等内容,适合DBA和开发人员在生产环境中使用。
Part01-基础概念与理论知识
1.1 PostgreSQL数据库PL/pgSQL存储过程概述
PL/pgSQL是PostgreSQL数据库内置的过程化语言,专门用于编写存储过程和函数。存储过程是一组预编译的SQL语句集合,可以接受输入参数、执行复杂业务逻辑并返回结果。更多视频教程www.fgedu.net.cn。PL/pgSQL存储过程将业务逻辑封装在数据库服务器端执行,减少了网络传输开销,提高了数据处理效率。
- 完全集成在PostgreSQL数据库中,无需额外安装
- 支持变量、条件判断、循环等程序控制结构
- 支持事务控制,可以在存储过程中提交或回滚事务
- 支持异常处理机制,保证业务逻辑健壮性
- 支持动态SQL执行,灵活处理复杂业务场景
1.2 PostgreSQL数据库业务逻辑封装优势
将业务逻辑封装为存储过程具有多方面优势:首先是性能优化,存储过程在数据库服务器端执行,避免了大量数据的网络传输;其次是安全性增强,可以通过存储过程控制数据访问权限,隐藏底层表结构;第三是代码复用,存储过程可以被多个应用程序调用,减少重复代码;第四是维护便捷,业务逻辑变更只需修改存储过程,无需修改应用程序代码。
1.3 PostgreSQL数据库存储过程与函数区别
PostgreSQL数据库中存储过程(PROCEDURE)和函数(FUNCTION)有重要区别:存储过程使用CALL语句调用,可以包含事务控制语句(COMMIT/ROLLBACK),不强制返回值;函数在SQL语句中直接调用,不能包含事务控制语句,必须返回值。学习交流加群风哥微信: itpux-com。存储过程更适合处理复杂业务流程,函数更适合计算和查询操作。
— 函数示例:必须返回值
CREATE OR REPLACE FUNCTION fgedu_calculate_total(p_order_id INTEGER)
RETURNS NUMERIC AS $$
DECLARE
v_total NUMERIC;
BEGIN
SELECT SUM(quantity * price) INTO v_total
FROM fgedu_order_items
WHERE order_id = p_order_id;
RETURN v_total;
END;
$$ LANGUAGE plpgsql;
— 存储过程示例:可以包含事务控制
CREATE OR REPLACE PROCEDURE fgedu_process_order(p_order_id INTEGER)
AS $$
BEGIN
— 更新订单状态
UPDATE fgedu_orders SET status = ‘processed’ WHERE id = p_order_id;
— 提交事务
COMMIT;
END;
$$ LANGUAGE plpgsql;
Part02-生产环境规划与建议
2.1 PostgreSQL数据库存储过程设计原则
设计PostgreSQL数据库存储过程时应遵循以下原则:单一职责原则,每个存储过程只负责一个特定的业务功能;参数设计合理,输入参数要有默认值和验证逻辑;错误处理完善,使用异常捕获机制处理各种错误情况;性能优化,避免在循环中执行SQL语句,使用批量操作提高效率。
CREATE OR REPLACE PROCEDURE fgedu_transfer_funds(
p_from_account INTEGER,
p_to_account INTEGER,
p_amount NUMERIC(15,2),
p_remark TEXT DEFAULT NULL
)
AS $$
DECLARE
v_balance NUMERIC(15,2);
v_insufficient BOOLEAN;
BEGIN
— 参数验证
IF p_amount <= 0 THEN RAISE EXCEPTION '转账金额必须大于0'; END IF; IF p_from_account = p_to_account THEN RAISE EXCEPTION '转出账户和转入账户不能相同'; END IF; -- 检查余额 SELECT balance INTO v_balance FROM fgedu_accounts WHERE id = p_from_account FOR UPDATE; IF v_balance < p_amount THEN RAISE EXCEPTION '账户余额不足,当前余额:%', v_balance; END IF; -- 执行转账 UPDATE fgedu_accounts SET balance = balance - p_amount WHERE id = p_from_account; UPDATE fgedu_accounts SET balance = balance + p_amount WHERE id = p_to_account; -- 记录日志 INSERT INTO fgedu_transfer_logs(from_account, to_account, amount, remark, create_time) VALUES(p_from_account, p_to_account, p_amount, p_remark, NOW()); COMMIT; RAISE NOTICE '转账成功,金额:%', p_amount; END; $$ LANGUAGE plpgsql;
2.2 PostgreSQL数据库存储过程命名规范
生产环境中存储过程命名应遵循统一规范:使用fgedu_前缀标识业务存储过程;使用动词+名词组合描述功能,如fgedu_process_order、fgedu_generate_report;参数命名使用p_前缀;局部变量使用v_前缀;常量使用c_前缀。
2.3 PostgreSQL数据库存储过程性能考量
存储过程性能优化要点:减少数据库往返次数,将多个操作合并到一个存储过程中;使用批量操作替代循环单条处理;合理使用索引确保查询效率;避免在存储过程中使用动态SQL,除非必要;使用EXPLAIN ANALYZE分析存储过程中的SQL语句执行计划。
Part03-生产环境项目实施方案
3.1 PostgreSQL数据库创建业务存储过程
3.1.1 创建基础业务存储过程
CREATE TABLE IF NOT EXISTS fgedu_orders (
id SERIAL PRIMARY KEY,
order_no VARCHAR(50) NOT NULL,
customer_id INTEGER NOT NULL,
total_amount NUMERIC(15,2) DEFAULT 0,
status VARCHAR(20) DEFAULT ‘pending’,
create_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS fgedu_order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
price NUMERIC(15,2) NOT NULL,
subtotal NUMERIC(15,2) GENERATED ALWAYS AS (quantity * price) STORED
);
— 创建订单处理存储过程
CREATE OR REPLACE PROCEDURE fgedu_create_order(
p_customer_id INTEGER,
p_items JSONB,
OUT p_order_id INTEGER
)
AS $$
DECLARE
v_order_no VARCHAR(50);
v_item JSONB;
BEGIN
— 生成订单号
v_order_no := ‘ORD’ || TO_CHAR(NOW(), ‘YYYYMMDDHH24MISS’) || LPAD(p_customer_id::TEXT, 6, ‘0’);
— 创建订单
INSERT INTO fgedu_orders(order_no, customer_id, status)
VALUES(v_order_no, p_customer_id, ‘created’)
RETURNING id INTO p_order_id;
— 插入订单明细
FOR v_item IN SELECT * FROM jsonb_array_elements(p_items)
LOOP
INSERT INTO fgedu_order_items(order_id, product_id, quantity, price)
VALUES(
p_order_id,
(v_item->>’product_id’)::INTEGER,
(v_item->>’quantity’)::INTEGER,
(v_item->>’price’)::NUMERIC
);
END LOOP;
— 更新订单总金额
UPDATE fgedu_orders o
SET total_amount = (
SELECT SUM(subtotal) FROM fgedu_order_items WHERE order_id = o.id
)
WHERE id = p_order_id;
COMMIT;
RAISE NOTICE ‘订单创建成功,订单号:%,订单ID:%’, v_order_no, p_order_id;
END;
$$ LANGUAGE plpgsql;
3.1.2 创建带事务控制的存储过程
CREATE OR REPLACE PROCEDURE fgedu_deduct_inventory(
p_order_id INTEGER
)
AS $$
DECLARE
v_item RECORD;
v_stock INTEGER;
v_product_name VARCHAR(100);
BEGIN
— 遍历订单明细扣减库存
FOR v_item IN
SELECT oi.product_id, oi.quantity, p.name as product_name
FROM fgedu_order_items oi
JOIN fgedu_products p ON oi.product_id = p.id
WHERE oi.order_id = p_order_id
LOOP
— 检查库存
SELECT stock INTO v_stock
FROM fgedu_inventory
WHERE product_id = v_item.product_id
FOR UPDATE;
IF v_stock < v_item.quantity THEN RAISE EXCEPTION '产品%库存不足,当前库存:%,需要:%', v_item.product_name, v_stock, v_item.quantity; END IF; -- 扣减库存 UPDATE fgedu_inventory SET stock = stock - v_item.quantity, update_time = NOW() WHERE product_id = v_item.product_id; RAISE NOTICE '产品%库存扣减成功,扣减数量:%', v_item.product_name, v_item.quantity; END LOOP; -- 更新订单状态 UPDATE fgedu_orders SET status = 'confirmed' WHERE id = p_order_id; COMMIT; END; $$ LANGUAGE plpgsql;
3.2 PostgreSQL数据库调用存储过程
3.2.1 使用CALL命令调用存储过程
CALL fgedu_create_order(
1001,
‘[
{“product_id”: 101, “quantity”: 2, “price”: 99.00},
{“product_id”: 102, “quantity”: 1, “price”: 199.00},
{“product_id”: 103, “quantity”: 3, “price”: 49.00}
]’::jsonb,
NULL
);
— 输出结果
NOTICE: 订单创建成功,订单号:ORD202604071530251001,订单ID:1
CALL
3.2.2 在存储过程中调用其他存储过程
CREATE OR REPLACE PROCEDURE fgedu_complete_order_process(
p_customer_id INTEGER,
p_items JSONB
)
AS $$
DECLARE
v_order_id INTEGER;
BEGIN
— 步骤1:创建订单
CALL fgedu_create_order(p_customer_id, p_items, v_order_id);
— 步骤2:扣减库存
CALL fgedu_deduct_inventory(v_order_id);
— 步骤3:生成支付记录
INSERT INTO fgedu_payments(order_id, amount, status, create_time)
SELECT v_order_id, total_amount, ‘pending’, NOW()
FROM fgedu_orders WHERE id = v_order_id;
COMMIT;
RAISE NOTICE ‘订单处理完成,订单ID:%’, v_order_id;
END;
$$ LANGUAGE plpgsql;
— 调用完整流程
CALL fgedu_complete_order_process(
1002,
‘[
{“product_id”: 201, “quantity”: 1, “price”: 599.00},
{“product_id”: 202, “quantity”: 2, “price”: 149.00}
]’::jsonb
);
— 输出结果
NOTICE: 订单创建成功,订单号:ORD202604071531001002,订单ID:2
NOTICE: 产品PostgreSQL实战教程库存扣减成功,扣减数量:1
NOTICE: 产品数据库运维手册库存扣减成功,扣减数量:2
NOTICE: 订单处理完成,订单ID:2
CALL
3.3 PostgreSQL数据库管理存储过程
3.3.1 查看存储过程信息
SELECT
p.proname AS procedure_name,
pg_get_functiondef(p.oid) AS definition,
obj_description(p.oid) AS comment
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = ‘public’
AND p.proname LIKE ‘fgedu_%’
AND p.prokind = ‘p’;
— 输出结果
procedure_name | definition
———————-+————————————————————–
fgedu_create_order | CREATE OR REPLACE PROCEDURE public.fgedu_create_order…
fgedu_deduct_inventory | CREATE OR REPLACE PROCEDURE public.fgedu_deduct_inventory…
fgedu_complete_order_process | CREATE OR REPLACE PROCEDURE public.fgedu_complete_order_process…
(3 rows)
— 查看存储过程参数信息
SELECT
p.proname AS procedure_name,
p.pronargs AS arg_count,
p.proargnames AS arg_names,
p.proargtypes::regtype[] AS arg_types
FROM pg_proc p
WHERE p.proname = ‘fgedu_create_order’;
— 输出结果
procedure_name | arg_count | arg_names | arg_types
——————–+———–+—————————-+——————————
fgedu_create_order | 3 | {p_customer_id,p_items,p_order_id} | {integer,jsonb,integer}
(1 row)
3.3.2 修改和删除存储过程
CREATE OR REPLACE PROCEDURE fgedu_create_order(
p_customer_id INTEGER,
p_items JSONB,
p_remark TEXT DEFAULT NULL,
OUT p_order_id INTEGER
)
AS $$
DECLARE
v_order_no VARCHAR(50);
v_item JSONB;
BEGIN
v_order_no := ‘ORD’ || TO_CHAR(NOW(), ‘YYYYMMDDHH24MISS’) || LPAD(p_customer_id::TEXT, 6, ‘0’);
INSERT INTO fgedu_orders(order_no, customer_id, status)
VALUES(v_order_no, p_customer_id, ‘created’)
RETURNING id INTO p_order_id;
FOR v_item IN SELECT * FROM jsonb_array_elements(p_items)
LOOP
INSERT INTO fgedu_order_items(order_id, product_id, quantity, price)
VALUES(
p_order_id,
(v_item->>’product_id’)::INTEGER,
(v_item->>’quantity’)::INTEGER,
(v_item->>’price’)::NUMERIC
);
END LOOP;
UPDATE fgedu_orders o
SET total_amount = (SELECT SUM(subtotal) FROM fgedu_order_items WHERE order_id = o.id),
remark = p_remark
WHERE id = p_order_id;
COMMIT;
END;
$$ LANGUAGE plpgsql;
— 输出结果
CREATE PROCEDURE
— 删除存储过程
DROP PROCEDURE IF EXISTS fgedu_old_procedure(INTEGER);
— 输出结果
DROP PROCEDURE
Part04-生产案例与实战讲解
4.1 PostgreSQL数据库订单处理存储过程实战
本案例演示一个完整的订单处理流程,包括订单创建、库存检查、支付处理、订单状态更新等环节。学习交流加群风哥QQ113257174。
CREATE OR REPLACE PROCEDURE fgedu_process_order_complete(
p_customer_id INTEGER,
p_items JSONB,
p_payment_method VARCHAR(20),
OUT p_result JSONB
)
AS $$
DECLARE
v_order_id INTEGER;
v_order_no VARCHAR(50);
v_total_amount NUMERIC(15,2);
v_item JSONB;
v_stock INTEGER;
v_product_id INTEGER;
v_quantity INTEGER;
BEGIN
p_result := ‘{“success”: false}’::jsonb;
— 步骤1:生成订单号
v_order_no := ‘ORD’ || TO_CHAR(NOW(), ‘YYYYMMDDHH24MISS’) || LPAD(p_customer_id::TEXT, 6, ‘0’);
— 步骤2:检查库存
FOR v_item IN SELECT * FROM jsonb_array_elements(p_items)
LOOP
v_product_id := (v_item->>’product_id’)::INTEGER;
v_quantity := (v_item->>’quantity’)::INTEGER;
SELECT stock INTO v_stock
FROM fgedu_inventory
WHERE product_id = v_product_id
FOR UPDATE;
IF v_stock IS NULL THEN
p_result := jsonb_build_object(
‘success’, false,
‘error’, ‘产品不存在’,
‘product_id’, v_product_id
);
RETURN;
END IF;
IF v_stock < v_quantity THEN
p_result := jsonb_build_object(
'success', false,
'error', '库存不足',
'product_id', v_product_id,
'available', v_stock,
'required', v_quantity
);
RETURN;
END IF;
END LOOP;
-- 步骤3:创建订单
INSERT INTO fgedu_orders(order_no, customer_id, status)
VALUES(v_order_no, p_customer_id, 'created')
RETURNING id INTO v_order_id;
-- 步骤4:插入订单明细
FOR v_item IN SELECT * FROM jsonb_array_elements(p_items)
LOOP
INSERT INTO fgedu_order_items(order_id, product_id, quantity, price)
VALUES(
v_order_id,
(v_item->>’product_id’)::INTEGER,
(v_item->>’quantity’)::INTEGER,
(v_item->>’price’)::NUMERIC
);
— 扣减库存
UPDATE fgedu_inventory
SET stock = stock – (v_item->>’quantity’)::INTEGER
WHERE product_id = (v_item->>’product_id’)::INTEGER;
END LOOP;
— 步骤5:计算并更新总金额
SELECT SUM(subtotal) INTO v_total_amount
FROM fgedu_order_items WHERE order_id = v_order_id;
UPDATE fgedu_orders SET total_amount = v_total_amount WHERE id = v_order_id;
— 步骤6:创建支付记录
INSERT INTO fgedu_payments(order_id, amount, payment_method, status)
VALUES(v_order_id, v_total_amount, p_payment_method, ‘pending’);
— 步骤7:更新订单状态
UPDATE fgedu_orders SET status = ‘confirmed’ WHERE id = v_order_id;
— 返回成功结果
p_result := jsonb_build_object(
‘success’, true,
‘order_id’, v_order_id,
‘order_no’, v_order_no,
‘total_amount’, v_total_amount,
‘payment_method’, p_payment_method
);
COMMIT;
END;
$$ LANGUAGE plpgsql;
— 执行订单处理
CALL fgedu_process_order_complete(
1003,
‘[
{“product_id”: 301, “quantity”: 2, “price”: 299.00},
{“product_id”: 302, “quantity”: 1, “price”: 599.00}
]’::jsonb,
‘alipay’,
NULL
);
— 输出结果
p_result
————————————————————————————————————————————
{“success”: true, “order_id”: 3, “order_no”: “ORD202604071535001003”, “total_amount”: 1197.00, “payment_method”: “alipay”}
(1 row)
4.2 PostgreSQL数据库批量数据处理存储过程
本案例演示批量数据处理场景,包括批量插入、批量更新、批量删除等操作。更多学习教程公众号风哥教程itpux_com。
CREATE OR REPLACE PROCEDURE fgedu_batch_process_data(
p_operation VARCHAR(20),
p_data JSONB
)
AS $$
DECLARE
v_record JSONB;
v_count INTEGER := 0;
v_start_time TIMESTAMP;
BEGIN
v_start_time := clock_timestamp();
CASE p_operation
WHEN ‘insert’ THEN
FOR v_record IN SELECT * FROM jsonb_array_elements(p_data)
LOOP
INSERT INTO fgedu_products(name, price, category, stock)
VALUES(
v_record->>’name’,
(v_record->>’price’)::NUMERIC,
v_record->>’category’,
(v_record->>’stock’)::INTEGER
);
v_count := v_count + 1;
— 每100条提交一次
IF v_count % 100 = 0 THEN
COMMIT;
RAISE NOTICE ‘已处理%条记录’, v_count;
END IF;
END LOOP;
WHEN ‘update’ THEN
FOR v_record IN SELECT * FROM jsonb_array_elements(p_data)
LOOP
UPDATE fgedu_products
SET price = (v_record->>’price’)::NUMERIC,
update_time = NOW()
WHERE id = (v_record->>’id’)::INTEGER;
v_count := v_count + 1;
END LOOP;
WHEN ‘delete’ THEN
FOR v_record IN SELECT * FROM jsonb_array_elements(p_data)
LOOP
DELETE FROM fgedu_products
WHERE id = (v_record->>’id’)::INTEGER;
v_count := v_count + 1;
END LOOP;
ELSE
RAISE EXCEPTION ‘不支持的操作类型:%’, p_operation;
END CASE;
COMMIT;
RAISE NOTICE ‘批量处理完成,操作:%,处理数量:%,耗时:%’,
p_operation, v_count, clock_timestamp() – v_start_time;
END;
$$ LANGUAGE plpgsql;
— 执行批量插入
CALL fgedu_batch_process_data(‘insert’, ‘
[
{“name”: “PostgreSQL入门”, “price”: 59.00, “category”: “book”, “stock”: 100},
{“name”: “PostgreSQL进阶”, “price”: 89.00, “category”: “book”, “stock”: 80},
{“name”: “PostgreSQL实战”, “price”: 129.00, “category”: “book”, “stock”: 50}
]’::jsonb);
— 输出结果
NOTICE: 批量处理完成,操作:insert,处理数量:3,耗时:00:00:00.002
CALL
4.3 PostgreSQL数据库报表统计存储过程
本案例演示报表统计存储过程的实现,包括销售统计、客户分析等常见报表场景。from PostgreSQL视频:www.itpux.com。
CREATE OR REPLACE PROCEDURE fgedu_generate_sales_report(
p_start_date DATE,
p_end_date DATE,
INOUT p_report JSONB DEFAULT NULL
)
AS $$
DECLARE
v_total_orders INTEGER;
v_total_amount NUMERIC(15,2);
v_avg_order_amount NUMERIC(15,2);
v_top_products JSONB;
v_daily_stats JSONB;
BEGIN
— 统计总体数据
SELECT
COUNT(*),
COALESCE(SUM(total_amount), 0),
COALESCE(AVG(total_amount), 0)
INTO v_total_orders, v_total_amount, v_avg_order_amount
FROM fgedu_orders
WHERE create_time::DATE BETWEEN p_start_date AND p_end_date
AND status IN (‘confirmed’, ‘completed’);
— 统计热销产品TOP10
SELECT jsonb_agg(row_to_json(t))
INTO v_top_products
FROM (
SELECT
p.name as product_name,
SUM(oi.quantity) as total_quantity,
SUM(oi.subtotal) as total_amount
FROM fgedu_order_items oi
JOIN fgedu_products p ON oi.product_id = p.id
JOIN fgedu_orders o ON oi.order_id = o.id
WHERE o.create_time::DATE BETWEEN p_start_date AND p_end_date
AND o.status IN (‘confirmed’, ‘completed’)
GROUP BY p.id, p.name
ORDER BY total_quantity DESC
LIMIT 10
) t;
— 统计每日销售数据
SELECT jsonb_agg(row_to_json(t))
INTO v_daily_stats
FROM (
SELECT
create_time::DATE as sale_date,
COUNT(*) as order_count,
SUM(total_amount) as daily_amount
FROM fgedu_orders
WHERE create_time::DATE BETWEEN p_start_date AND p_end_date
AND status IN (‘confirmed’, ‘completed’)
GROUP BY create_time::DATE
ORDER BY sale_date
) t;
— 构建报表结果
p_report := jsonb_build_object(
‘report_date’, NOW()::DATE,
‘period’, jsonb_build_object(
‘start_date’, p_start_date,
‘end_date’, p_end_date
),
‘summary’, jsonb_build_object(
‘total_orders’, v_total_orders,
‘total_amount’, v_total_amount,
‘avg_order_amount’, v_avg_order_amount
),
‘top_products’, COALESCE(v_top_products, ‘[]’::jsonb),
‘daily_stats’, COALESCE(v_daily_stats, ‘[]’::jsonb)
);
END;
$$ LANGUAGE plpgsql;
— 执行报表统计
CALL fgedu_generate_sales_report(‘2026-04-01’, ‘2026-04-07’, NULL);
— 输出结果
p_report
—————————————————————————————————————————————————————————————————–
{“report_date”: “2026-04-07”, “period”: {“start_date”: “2026-04-01”, “end_date”: “2026-04-07”}, “summary”: {“total_orders”: 156, “total_amount”: 125680.50, “avg_order_amount”: 805.64}, “top_products”: [{“product_name”: “PostgreSQL实战教程”, “total_quantity”: 89, “total_amount”: 53110.00}, {“product_name”: “数据库运维手册”, “total_quantity”: 67, “total_amount”: 32165.00}], “daily_stats”: [{“sale_date”: “2026-04-01”, “order_count”: 23, “daily_amount”: 18560.00}, {“sale_date”: “2026-04-02”, “order_count”: 28, “daily_amount”: 22450.00}]}
(1 row)
Part05-风哥经验总结与分享
5.1 PostgreSQL数据库存储过程最佳实践
在生产环境中使用存储过程应注意以下最佳实践:首先,存储过程应有完善的文档注释,说明功能、参数、返回值;其次,使用适当的异常处理机制,确保错误能够被捕获和记录;第三,避免在存储过程中使用SELECT *,明确指定需要的字段;第四,对于大数据量操作,使用分批处理避免长事务;第五,定期检查和优化存储过程性能。
CREATE OR REPLACE PROCEDURE fgedu_best_practice_example(
p_param1 INTEGER,
p_param2 VARCHAR(100) DEFAULT NULL
)
LANGUAGE plpgsql
AS $$
/*
* 存储过程名称:fgedu_best_practice_example
* 功能描述:演示存储过程最佳实践
* 参数说明:
* p_param1 – 第一个参数说明
* p_param2 – 第二个参数说明(可选)
* 返回值:无
* 创建时间:2026-04-07
* 修改记录:
* 2026-04-07 – 初始创建
*/
DECLARE
v_count INTEGER;
v_start_time TIMESTAMP;
BEGIN
v_start_time := clock_timestamp();
— 参数验证
IF p_param1 IS NULL THEN
RAISE EXCEPTION ‘参数p_param1不能为空’;
END IF;
— 业务逻辑处理
BEGIN
— 明确指定字段,避免SELECT *
SELECT COUNT(*) INTO v_count
FROM fgedu_table
WHERE id = p_param1;
— 记录处理日志
INSERT INTO fgedu_proc_logs(proc_name, params, row_count, exec_time, create_time)
VALUES(
‘fgedu_best_practice_example’,
jsonb_build_object(‘p_param1’, p_param1, ‘p_param2’, p_param2),
v_count,
clock_timestamp() – v_start_time,
NOW()
);
EXCEPTION
WHEN OTHERS THEN
— 记录错误日志
INSERT INTO fgedu_error_logs(proc_name, error_message, create_time)
VALUES(‘fgedu_best_practice_example’, SQLERRM, NOW());
RAISE;
END;
COMMIT;
END;
$$;
5.2 PostgreSQL数据库存储过程调试技巧
调试存储过程时可以使用以下技巧:使用RAISE NOTICE输出调试信息;使用pg_stat_statements查看存储过程执行统计;使用EXPLAIN ANALYZE分析存储过程中的SQL语句;将复杂存储过程拆分为多个小存储过程逐步调试。
CREATE OR REPLACE PROCEDURE fgedu_debug_example(p_id INTEGER)
AS $$
DECLARE
v_data RECORD;
v_count INTEGER;
BEGIN
— 使用RAISE NOTICE输出调试信息
RAISE NOTICE ‘开始处理,参数p_id=%’, p_id;
— 检查数据
SELECT COUNT(*) INTO v_count FROM fgedu_orders WHERE customer_id = p_id;
RAISE NOTICE ‘找到%条订单记录’, v_count;
— 处理数据
FOR v_data IN SELECT * FROM fgedu_orders WHERE customer_id = p_id
LOOP
RAISE NOTICE ‘处理订单ID=%,订单号=%’, v_data.id, v_data.order_no;
— 业务处理逻辑
END LOOP;
RAISE NOTICE ‘处理完成’;
END;
$$ LANGUAGE plpgsql;
— 执行调试
CALL fgedu_debug_example(1001);
— 输出结果
NOTICE: 开始处理,参数p_id=1001
NOTICE: 找到3条订单记录
NOTICE: 处理订单ID=1,订单号=ORD202604071530251001
NOTICE: 处理订单ID=4,订单号=ORD202604071532001001
NOTICE: 处理订单ID=7,订单号=ORD202604071534001001
NOTICE: 处理完成
CALL
5.3 PostgreSQL数据库存储过程常见问题
存储过程开发中常见问题包括:事务控制不当导致数据不一致;异常处理不完善导致错误信息丢失;参数类型不匹配导致调用失败;性能问题导致执行超时。针对这些问题,需要仔细设计存储过程逻辑,完善异常处理机制,并进行充分的测试。
- 事务问题:明确事务边界,合理使用COMMIT和ROLLBACK
- 异常处理:使用EXCEPTION块捕获并记录错误
- 参数问题:使用明确的参数类型,添加参数验证
- 性能问题:优化SQL语句,使用批量操作,添加适当索引
CREATE OR REPLACE PROCEDURE fgedu_robust_procedure(
p_data JSONB
)
AS $$
DECLARE
v_id INTEGER;
v_name VARCHAR(100);
BEGIN
— 参数验证
IF p_data IS NULL THEN
RAISE EXCEPTION ‘输入数据不能为空’;
END IF;
— 类型安全转换
BEGIN
v_id := (p_data->>’id’)::INTEGER;
v_name := p_data->>’name’;
EXCEPTION
WHEN INVALID_TEXT_REPRESENTATION THEN
RAISE EXCEPTION ‘数据格式错误,id必须是整数’;
END;
— 业务处理(带异常处理)
BEGIN
INSERT INTO fgedu_target_table(id, name, create_time)
VALUES(v_id, v_name, NOW());
COMMIT;
RAISE NOTICE ‘数据处理成功’;
EXCEPTION
WHEN UNIQUE_VIOLATION THEN
RAISE NOTICE ‘记录已存在,执行更新’;
UPDATE fgedu_target_table
SET name = v_name, update_time = NOW()
WHERE id = v_id;
COMMIT;
WHEN OTHERS THEN
RAISE EXCEPTION ‘数据处理失败:%’, SQLERRM;
END;
END;
$$ LANGUAGE plpgsql;
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
