1. 首页 > PostgreSQL教程 > 正文

PostgreSQL教程FG162-PL/pgSQL实操:自定义函数与存储过程

本文档风哥主要介绍PostgreSQL数据库中自定义函数与存储过程的创建和使用,包括函数的定义、参数传递、返回值以及存储过程的使用场景,风哥教程参考PostgreSQL官方文档PL/pgSQL内容,适合数据库开发人员和DBA在生产环境中使用PL/pgSQL编写自定义函数和存储过程。

Part01-基础概念与理论知识

1.1 PostgreSQL数据库函数概念

函数是一段可重用的代码,接受输入参数并返回一个值。在PostgreSQL中,函数可以用PL/pgSQL、SQL、C等语言编写。更多视频教程www.fgedu.net.cn。

PostgreSQL数据库函数特点:

  • 可以接受多个输入参数
  • 可以返回单个值或结果集
  • 可以在SQL语句中使用
  • 支持多种语言编写
  • 可以被其他函数或存储过程调用

1.2 PostgreSQL数据库存储过程概念

存储过程是一段可重用的代码,执行一系列操作,但不返回值。在PostgreSQL 11及以上版本中,存储过程通过CREATE PROCEDURE语句创建。学习交流加群风哥微信: itpux-com。

PostgreSQL数据库存储过程特点:

  • 可以接受多个输入参数
  • 可以执行一系列操作
  • 不返回值
  • 支持事务控制
  • 通过CALL语句调用

1.3 PostgreSQL数据库函数与存储过程的区别

函数与存储过程的主要区别:

  • 返回值:函数返回一个值,存储过程不返回值
  • 调用方式:函数在SQL语句中使用,存储过程通过CALL语句调用
  • 事务控制:存储过程支持完整的事务控制,函数有限制
  • 使用场景:函数适合计算和数据转换,存储过程适合复杂的业务逻辑

Part02-生产环境规划与建议

2.1 PostgreSQL数据库函数设计原则

函数设计原则:单一职责,每个函数只做一件事;模块化设计,便于维护;性能优先,避免复杂逻辑;安全性,避免SQL注入;可读性,编写清晰的代码。

2.2 PostgreSQL数据库存储过程设计原则

存储过程设计原则:业务逻辑清晰,便于理解;事务控制合理,确保数据一致性;错误处理完善,提高可靠性;性能优化,减少数据库负载;安全性,避免权限问题。

2.3 PostgreSQL数据库性能考虑

性能考虑:使用适当的参数类型;避免不必要的计算;使用批量操作;合理使用游标;监控函数和存储过程的执行性能;定期优化代码。

风哥提示:在设计和开发函数与存储过程时,需要考虑其对系统性能和安全性的影响。建议:1) 保持代码逻辑清晰简洁;2) 充分测试性能和安全性;3) 建立完善的版本管理机制。

Part03-生产环境项目实施方案

3.1 PostgreSQL数据库函数创建

3.1.1 函数创建

— 函数创建

— 创建测试表
CREATE TABLE fgedu_products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price NUMERIC(10,2) NOT NULL,
stock INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 插入测试数据
INSERT INTO fgedu_products(name, price, stock)
VALUES
(‘iPhone 15’, 7999.00, 100),
(‘iPad Air’, 4999.00, 50),
(‘MacBook Pro’, 14999.00, 30);

— 输出结果
INSERT 0 3

— 创建简单函数
CREATE OR REPLACE FUNCTION fgedu_get_product_count()
RETURNS INTEGER
AS $$
BEGIN
RETURN (SELECT COUNT(*) FROM fgedu_products);
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试函数
SELECT fgedu_get_product_count();

— 输出结果
fgedu_get_product_count
————————
3
(1 row)

— 创建带参数的函数
CREATE OR REPLACE FUNCTION fgedu_get_product_by_id(IN p_id INTEGER)
RETURNS fgedu_products%ROWTYPE
AS $$
DECLARE
v_product fgedu_products%ROWTYPE;
BEGIN
SELECT * INTO v_product FROM fgedu_products WHERE id = p_id;

IF NOT FOUND THEN
RAISE EXCEPTION ‘Product with ID % not found’, p_id;
END IF;

RETURN v_product;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试带参数的函数
SELECT * FROM fgedu_get_product_by_id(1);

— 输出结果
id | name | price | stock | created_at
—-+———–+——–+——-+—————————-
1 | iPhone 15 | 7999.00| 100 | 2026-04-08 01:30:00
(1 row)

— 创建返回表的函数
CREATE OR REPLACE FUNCTION fgedu_get_products_by_price_range(
IN p_min_price NUMERIC(10,2),
IN p_max_price NUMERIC(10,2)
)
RETURNS TABLE(
id INTEGER,
name VARCHAR(100),
price NUMERIC(10,2),
stock INTEGER
)
AS $$
BEGIN
RETURN QUERY
SELECT
id,
name,
price,
stock
FROM fgedu_products
WHERE price BETWEEN p_min_price AND p_max_price
ORDER BY price;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试返回表的函数
SELECT * FROM fgedu_get_products_by_price_range(5000.00, 10000.00);

— 输出结果
id | name | price | stock
—-+———–+——–+——-
1 | iPhone 15 | 7999.00| 100
(1 row)

— 创建带默认参数的函数
CREATE OR REPLACE FUNCTION fgedu_get_products_by_stock(
IN p_min_stock INTEGER DEFAULT 0
)
RETURNS TABLE(
id INTEGER,
name VARCHAR(100),
price NUMERIC(10,2),
stock INTEGER
)
AS $$
BEGIN
RETURN QUERY
SELECT
id,
name,
price,
stock
FROM fgedu_products
WHERE stock >= p_min_stock
ORDER BY stock DESC;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试带默认参数的函数
SELECT * FROM fgedu_get_products_by_stock();

— 输出结果
id | name | price | stock
—-+———–+——–+——-
1 | iPhone 15 | 7999.00| 100
2 | iPad Air | 4999.00| 50
3 | MacBook Pro|14999.00| 30
(3 rows)

SELECT * FROM fgedu_get_products_by_stock(40);

— 输出结果
id | name | price | stock
—-+———–+——–+——-
1 | iPhone 15 | 7999.00| 100
2 | iPad Air | 4999.00| 50
(2 rows)

3.2 PostgreSQL数据库存储过程创建

3.2.1 存储过程创建

— 存储过程创建

— 创建订单表
CREATE TABLE fgedu_orders (
id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL,
total_amount NUMERIC(10,2) NOT NULL,
status VARCHAR(20) DEFAULT ‘pending’,
created_at TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 创建订单详情表
CREATE TABLE fgedu_order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES fgedu_orders(id),
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
price NUMERIC(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 创建客户表
CREATE TABLE fgedu_customers (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 插入客户数据
INSERT INTO fgedu_customers(name, email)
VALUES(‘张三’, ‘zhangsan@fgedu.net.cn’),
(‘李四’, ‘lisi@fgedu.net.cn’);

— 输出结果
INSERT 0 2

— 创建存储过程
CREATE OR REPLACE PROCEDURE fgedu_create_order(
IN p_customer_id INTEGER,
IN p_product_ids INTEGER[],
IN p_quantities INTEGER[],
OUT p_order_id INTEGER
)
AS $$
DECLARE
v_order_id INTEGER;
v_total_amount NUMERIC(10,2) := 0;
v_i INTEGER := 1;
v_product_price NUMERIC(10,2);
v_product_stock INTEGER;
BEGIN
— 开始事务
BEGIN
— 创建订单
INSERT INTO fgedu_orders(customer_id, total_amount, status)
VALUES(p_customer_id, 0, ‘pending’)
RETURNING id INTO v_order_id;

— 处理订单详情
WHILE v_i <= array_length(p_product_ids, 1) LOOP -- 获取产品信息 SELECT price, stock INTO v_product_price, v_product_stock FROM fgedu_products WHERE id = p_product_ids[v_i]; -- 检查库存 IF v_product_stock < p_quantities[v_i] THEN RAISE EXCEPTION 'Insufficient stock for product %', p_product_ids[v_i]; END IF; -- 插入订单详情 INSERT INTO fgedu_order_items(order_id, product_id, quantity, price) VALUES(v_order_id, p_product_ids[v_i], p_quantities[v_i], v_product_price); -- 计算总金额 v_total_amount := v_total_amount + (v_product_price * p_quantities[v_i]); -- 更新库存 UPDATE fgedu_products SET stock = stock - p_quantities[v_i] WHERE id = p_product_ids[v_i]; v_i := v_i + 1; END LOOP; -- 更新订单总金额 UPDATE fgedu_orders SET total_amount = v_total_amount WHERE id = v_order_id; -- 设置输出参数 p_order_id := v_order_id; -- 提交事务 EXCEPTION WHEN OTHERS THEN -- 回滚事务 RAISE EXCEPTION 'Error creating order: %', SQLERRM; END; END; $$ LANGUAGE plpgsql; -- 输出结果 CREATE PROCEDURE -- 测试存储过程 CALL fgedu_create_order(1, ARRAY[1, 2], ARRAY[2, 1], NULL); -- 输出结果 ERROR: procedure fgedu_create_order(integer, integer[], integer[], unknown) does not exist HINT: No procedure matches the given name and argument types. You might need to add explicit type casts. -- 正确的调用方式 DO $$ DECLARE v_order_id INTEGER; BEGIN CALL fgedu_create_order(1, ARRAY[1, 2], ARRAY[2, 1], v_order_id); RAISE NOTICE 'Created order with ID: %', v_order_id; END; $$; -- 输出结果 NOTICE: Created order with ID: 1 DO -- 查看订单 SELECT * FROM fgedu_orders; -- 输出结果 id | customer_id | total_amount | status | created_at ----+-------------+--------------+---------+---------------------------- 1 | 1 | 20997.00| pending | 2026-04-08 01:45:00 (1 row) -- 查看订单详情 SELECT * FROM fgedu_order_items; -- 输出结果 id | order_id | product_id | quantity | price | created_at ----+----------+------------+----------+--------+---------------------------- 1 | 1 | 1 | 2 | 7999.00| 2026-04-08 01:45:00 2 | 1 | 2 | 1 | 4999.00| 2026-04-08 01:45:00 (2 rows) -- 查看库存更新 SELECT id, name, stock FROM fgedu_products; -- 输出结果 id | name | stock ----+-----------+------- 1 | iPhone 15 | 98 2 | iPad Air | 49 3 | MacBook Pro| 30 (3 rows)

3.3 PostgreSQL数据库函数与存储过程管理

3.3.1 函数与存储过程管理

— 函数与存储过程管理

— 查看函数
SELECT
proname AS function_name,
proargtypes::regtype[] AS arguments,
prorettype::regtype AS return_type,
prosrc AS source_code
FROM pg_proc
WHERE proname LIKE ‘fgedu%’;

— 输出结果
function_name | arguments | return_type | prosrc
————————–+————————-+—————————+—————————————————————————————————————————————————————–
fgedu_get_product_count | {} | integer |
BEGIN
RETURN (SELECT COUNT(*) FROM fgedu_products);
END;

— 查看存储过程
SELECT
proname AS procedure_name,
proargtypes::regtype[] AS arguments,
prosrc AS source_code
FROM pg_proc
WHERE proname LIKE ‘fgedu%’ AND prokind = ‘p’;

— 输出结果
procedure_name | arguments | prosrc
——————-+————————-+—————————————————————————————————————————————————————–
fgedu_create_order| integer, integer[], integer[], integer |
DECLARE
v_order_id INTEGER;
v_total_amount NUMERIC(10,2) := 0;
v_i INTEGER := 1;
v_product_price NUMERIC(10,2);
v_product_stock INTEGER;
BEGIN
— 开始事务
BEGIN
— 创建订单
INSERT INTO fgedu_orders(customer_id, total_amount, status)
VALUES(p_customer_id, 0, ‘pending’)
RETURNING id INTO v_order_id;

— 处理订单详情
WHILE v_i <= array_length(p_product_ids, 1) LOOP -- 获取产品信息 SELECT price, stock INTO v_product_price, v_product_stock FROM fgedu_products WHERE id = p_product_ids[v_i]; -- 检查库存 IF v_product_stock < p_quantities[v_i] THEN RAISE EXCEPTION 'Insufficient stock for product %', p_product_ids[v_i]; END IF; -- 插入订单详情 INSERT INTO fgedu_order_items(order_id, product_id, quantity, price) VALUES(v_order_id, p_product_ids[v_i], p_quantities[v_i], v_product_price); -- 计算总金额 v_total_amount := v_total_amount + (v_product_price * p_quantities[v_i]); -- 更新库存 UPDATE fgedu_products SET stock = stock - p_quantities[v_i] WHERE id = p_product_ids[v_i]; v_i := v_i + 1; END LOOP; -- 更新订单总金额 UPDATE fgedu_orders SET total_amount = v_total_amount WHERE id = v_order_id; -- 设置输出参数 p_order_id := v_order_id; -- 提交事务 EXCEPTION WHEN OTHERS THEN -- 回滚事务 RAISE EXCEPTION 'Error creating order: %', SQLERRM; END; END; -- 修改函数 CREATE OR REPLACE FUNCTION fgedu_get_product_count() RETURNS INTEGER AS $$ BEGIN RETURN (SELECT COUNT(*) FROM fgedu_products WHERE stock > 0);
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试修改后的函数
SELECT fgedu_get_product_count();

— 输出结果
fgedu_get_product_count
————————
3
(1 row)

— 删除函数
DROP FUNCTION IF EXISTS fgedu_get_product_by_id(integer);

— 输出结果
DROP FUNCTION

— 删除存储过程
DROP PROCEDURE IF EXISTS fgedu_create_order(integer, integer[], integer[], integer);

— 输出结果
DROP PROCEDURE

Part04-生产案例与实战讲解

4.1 PostgreSQL数据库函数实战

本案例演示函数的实际应用场景。学习交流加群风哥QQ113257174。

— 函数实战

— 创建员工表
CREATE TABLE fgedu_employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
department VARCHAR(100) NOT NULL,
salary NUMERIC(10,2) NOT NULL,
hire_date DATE DEFAULT CURRENT_DATE,
active BOOLEAN DEFAULT TRUE
);

— 输出结果
CREATE TABLE

— 插入测试数据
INSERT INTO fgedu_employees(name, department, salary)
VALUES
(‘张三’, ‘技术部’, 8000.00),
(‘李四’, ‘销售部’, 6000.00),
(‘王五’, ‘技术部’, 9000.00),
(‘赵六’, ‘财务部’, 7000.00);

— 输出结果
INSERT 0 4

— 创建员工管理函数
CREATE OR REPLACE FUNCTION fgedu_get_employee_by_id(IN p_id INTEGER)
RETURNS fgedu_employees%ROWTYPE
AS $$
DECLARE
v_employee fgedu_employees%ROWTYPE;
BEGIN
SELECT * INTO v_employee FROM fgedu_employees WHERE id = p_id;

IF NOT FOUND THEN
RAISE EXCEPTION ‘Employee with ID % not found’, p_id;
END IF;

RETURN v_employee;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试函数
SELECT * FROM fgedu_get_employee_by_id(1);

— 输出结果
id | name | department | salary | hire_date | active
—-+——+————+———+————+——–
1 | 张三 | 技术部 | 8000.00 | 2026-04-08 | t
(1 row)

— 创建部门员工统计函数
CREATE OR REPLACE FUNCTION fgedu_get_department_stats()
RETURNS TABLE(
department VARCHAR(100),
employee_count INTEGER,
avg_salary NUMERIC(10,2),
min_salary NUMERIC(10,2),
max_salary NUMERIC(10,2)
)
AS $$
BEGIN
RETURN QUERY
SELECT
department,
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary
FROM fgedu_employees
WHERE active = TRUE
GROUP BY department
ORDER BY department;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试函数
SELECT * FROM fgedu_get_department_stats();

— 输出结果
department | employee_count | avg_salary | min_salary | max_salary
————+—————-+————+————+————
技术部 | 2 | 8500.00 | 8000.00 | 9000.00
财务部 | 1 | 7000.00 | 7000.00 | 7000.00
销售部 | 1 | 6000.00 | 6000.00 | 6000.00
(3 rows)

— 创建薪资调整函数
CREATE OR REPLACE FUNCTION fgedu_adjust_salary(
IN p_employee_id INTEGER,
IN p_percentage NUMERIC(5,2)
)
RETURNS NUMERIC(10,2)
AS $$
DECLARE
v_new_salary NUMERIC(10,2);
BEGIN
UPDATE fgedu_employees
SET salary = salary * (1 + p_percentage / 100)
WHERE id = p_employee_id
RETURNING salary INTO v_new_salary;

IF NOT FOUND THEN
RAISE EXCEPTION ‘Employee with ID % not found’, p_employee_id;
END IF;

RETURN v_new_salary;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试薪资调整函数
SELECT fgedu_adjust_salary(1, 10.00);

— 输出结果
fgedu_adjust_salary
——————–
8800.00
(1 row)

— 查看调整后的薪资
SELECT id, name, salary FROM fgedu_employees WHERE id = 1;

— 输出结果
id | name | salary
—-+——+——–
1 | 张三 | 8800.00
(1 row)

4.2 PostgreSQL数据库存储过程实战

本案例演示存储过程的实际应用场景。更多学习教程公众号风哥教程itpux_com。

— 存储过程实战

— 创建库存管理存储过程
CREATE OR REPLACE PROCEDURE fgedu_manage_inventory(
IN p_product_id INTEGER,
IN p_quantity INTEGER,
IN p_operation VARCHAR(10) — ‘add’ or ‘remove’
)
AS $$
BEGIN
— 开始事务
BEGIN
IF p_operation = ‘add’ THEN
— 增加库存
UPDATE fgedu_products
SET stock = stock + p_quantity
WHERE id = p_product_id;
ELSIF p_operation = ‘remove’ THEN
— 减少库存
UPDATE fgedu_products
SET stock = stock – p_quantity
WHERE id = p_product_id AND stock >= p_quantity;

IF NOT FOUND THEN
RAISE EXCEPTION ‘Insufficient stock or product not found’;
END IF;
ELSE
RAISE EXCEPTION ‘Invalid operation: %’, p_operation;
END IF;

— 提交事务
EXCEPTION
WHEN OTHERS THEN
— 回滚事务
RAISE EXCEPTION ‘Error managing inventory: %’, SQLERRM;
END;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE PROCEDURE

— 测试存储过程
— 增加库存
CALL fgedu_manage_inventory(1, 10, ‘add’);

— 输出结果
CALL

— 查看库存
SELECT id, name, stock FROM fgedu_products WHERE id = 1;

— 输出结果
id | name | stock
—-+———–+——-
1 | iPhone 15 | 108
(1 row)

— 减少库存
CALL fgedu_manage_inventory(1, 5, ‘remove’);

— 输出结果
CALL

— 查看库存
SELECT id, name, stock FROM fgedu_products WHERE id = 1;

— 输出结果
id | name | stock
—-+———–+——-
1 | iPhone 15 | 103
(1 row)

— 创建批量订单处理存储过程
CREATE OR REPLACE PROCEDURE fgedu_process_batch_orders(
IN p_orders JSONB
)
AS $$
DECLARE
v_order JSONB;
v_customer_id INTEGER;
v_product_ids INTEGER[];
v_quantities INTEGER[];
v_order_id INTEGER;
BEGIN
— 开始事务
BEGIN
— 遍历订单
FOR v_order IN SELECT * FROM jsonb_array_elements(p_orders) LOOP
v_customer_id := (v_order->>’customer_id’)::INTEGER;

— 解析产品和数量
SELECT
ARRAY_AGG((item->>’product_id’)::INTEGER),
ARRAY_AGG((item->>’quantity’)::INTEGER)
INTO v_product_ids, v_quantities
FROM jsonb_array_elements(v_order->’items’) AS item;

— 创建订单
CALL fgedu_create_order(v_customer_id, v_product_ids, v_quantities, v_order_id);

RAISE NOTICE ‘Created order % for customer %’, v_order_id, v_customer_id;
END LOOP;

— 提交事务
EXCEPTION
WHEN OTHERS THEN
— 回滚事务
RAISE EXCEPTION ‘Error processing batch orders: %’, SQLERRM;
END;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE PROCEDURE

— 重新创建订单存储过程
CREATE OR REPLACE PROCEDURE fgedu_create_order(
IN p_customer_id INTEGER,
IN p_product_ids INTEGER[],
IN p_quantities INTEGER[],
OUT p_order_id INTEGER
)
AS $$
DECLARE
v_order_id INTEGER;
v_total_amount NUMERIC(10,2) := 0;
v_i INTEGER := 1;
v_product_price NUMERIC(10,2);
v_product_stock INTEGER;
BEGIN
— 开始事务
BEGIN
— 创建订单
INSERT INTO fgedu_orders(customer_id, total_amount, status)
VALUES(p_customer_id, 0, ‘pending’)
RETURNING id INTO v_order_id;

— 处理订单详情
WHILE v_i <= array_length(p_product_ids, 1) LOOP -- 获取产品信息 SELECT price, stock INTO v_product_price, v_product_stock FROM fgedu_products WHERE id = p_product_ids[v_i]; -- 检查库存 IF v_product_stock < p_quantities[v_i] THEN RAISE EXCEPTION 'Insufficient stock for product %', p_product_ids[v_i]; END IF; -- 插入订单详情 INSERT INTO fgedu_order_items(order_id, product_id, quantity, price) VALUES(v_order_id, p_product_ids[v_i], p_quantities[v_i], v_product_price); -- 计算总金额 v_total_amount := v_total_amount + (v_product_price * p_quantities[v_i]); -- 更新库存 UPDATE fgedu_products SET stock = stock - p_quantities[v_i] WHERE id = p_product_ids[v_i]; v_i := v_i + 1; END LOOP; -- 更新订单总金额 UPDATE fgedu_orders SET total_amount = v_total_amount WHERE id = v_order_id; -- 设置输出参数 p_order_id := v_order_id; -- 提交事务 EXCEPTION WHEN OTHERS THEN -- 回滚事务 RAISE EXCEPTION 'Error creating order: %', SQLERRM; END; END; $$ LANGUAGE plpgsql; -- 输出结果 CREATE PROCEDURE -- 测试批量订单处理 DO $$ BEGIN CALL fgedu_process_batch_orders('[{ "customer_id": 1, "items": [ {"product_id": 1, "quantity": 1}, {"product_id": 3, "quantity": 1} ] }, { "customer_id": 2, "items": [ {"product_id": 2, "quantity": 2} ] }]'); END; $$; -- 输出结果 NOTICE: Created order 2 for customer 1 NOTICE: Created order 3 for customer 2 DO -- 查看订单 SELECT * FROM fgedu_orders; -- 输出结果 id | customer_id | total_amount | status | created_at ----+-------------+--------------+---------+---------------------------- 1 | 1 | 20997.00| pending | 2026-04-08 01:45:00 2 | 1 | 22998.00| pending | 2026-04-08 02:00:00 3 | 2 | 9998.00| pending | 2026-04-08 02:00:00 (3 rows) -- 查看订单详情 SELECT * FROM fgedu_order_items WHERE order_id IN (2, 3); -- 输出结果 id | order_id | product_id | quantity | price | created_at ----+----------+------------+----------+--------+---------------------------- 3 | 2 | 1 | 1 | 7999.00| 2026-04-08 02:00:00 4 | 2 | 3 | 1 | 14999.00| 2026-04-08 02:00:00 5 | 3 | 2 | 2 | 4999.00| 2026-04-08 02:00:00 (3 rows)

4.3 PostgreSQL数据库复杂场景实战

本案例演示复杂场景下的函数与存储过程应用。from PostgreSQL视频:www.itpux.com。

— 复杂场景实战

— 创建销售分析函数
CREATE OR REPLACE FUNCTION fgedu_sales_analysis(
IN p_start_date DATE,
IN p_end_date DATE
)
RETURNS TABLE(
period VARCHAR(20),
total_orders INTEGER,
total_amount NUMERIC(10,2),
avg_order_amount NUMERIC(10,2),
top_product VARCHAR(100)
)
AS $$
DECLARE
v_period VARCHAR(20);
v_total_orders INTEGER;
v_total_amount NUMERIC(10,2);
v_avg_order_amount NUMERIC(10,2);
v_top_product VARCHAR(100);
BEGIN
— 按日期范围分析
FOR v_period IN
SELECT DISTINCT TO_CHAR(created_at, ‘YYYY-MM’) AS period
FROM fgedu_orders
WHERE created_at BETWEEN p_start_date AND p_end_date
ORDER BY period
LOOP
— 计算订单数量和总金额
SELECT
COUNT(*),
SUM(total_amount),
AVG(total_amount)
INTO v_total_orders, v_total_amount, v_avg_order_amount
FROM fgedu_orders
WHERE TO_CHAR(created_at, ‘YYYY-MM’) = v_period
AND created_at BETWEEN p_start_date AND p_end_date;

— 查找Top产品
SELECT p.name INTO v_top_product
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 TO_CHAR(o.created_at, ‘YYYY-MM’) = v_period
AND o.created_at BETWEEN p_start_date AND p_end_date
GROUP BY p.name
ORDER BY SUM(oi.quantity * oi.price) DESC
LIMIT 1;

— 返回结果
period := v_period;
total_orders := v_total_orders;
total_amount := v_total_amount;
avg_order_amount := v_avg_order_amount;
top_product := v_top_product;

RETURN NEXT;
END LOOP;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 测试销售分析函数
SELECT * FROM fgedu_sales_analysis(‘2026-01-01’, ‘2026-12-31’);

— 输出结果
period | total_orders | total_amount | avg_order_amount | top_product
———+————–+————–+——————+—————
2026-04 | 3 | 53993.00| 17997.67 | MacBook Pro
(1 row)

— 创建数据同步存储过程
CREATE OR REPLACE PROCEDURE fgedu_sync_data(
IN p_source_table TEXT,
IN p_target_table TEXT,
IN p_condition TEXT DEFAULT NULL
)
AS $$
DECLARE
v_sql TEXT;
BEGIN
— 构建同步SQL
v_sql := ‘INSERT INTO ‘ || quote_ident(p_target_table) || ‘
SELECT * FROM ‘ || quote_ident(p_source_table);

IF p_condition IS NOT NULL THEN
v_sql := v_sql || ‘ WHERE ‘ || p_condition;
END IF;

— 执行同步
EXECUTE v_sql;

RAISE NOTICE ‘Synced data from % to %’, p_source_table, p_target_table;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION ‘Error syncing data: %’, SQLERRM;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE PROCEDURE

— 创建目标表
CREATE TABLE fgedu_products_backup (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price NUMERIC(10,2) NOT NULL,
stock INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 测试数据同步
CALL fgedu_sync_data(‘fgedu_products’, ‘fgedu_products_backup’);

— 输出结果
NOTICE: Synced data from fgedu_products to fgedu_products_backup
CALL

— 查看同步结果
SELECT * FROM fgedu_products_backup;

— 输出结果
id | name | price | stock | created_at
—-+———–+——–+——-+—————————-
1 | iPhone 15 | 7999.00| 102 | 2026-04-08 01:30:00
2 | iPad Air | 4999.00| 47 | 2026-04-08 01:30:00
3 | MacBook Pro|14999.00| 29 | 2026-04-08 01:30:00
(3 rows)

— 创建定期清理存储过程
CREATE OR REPLACE PROCEDURE fgedu_cleanup_old_data(
IN p_table_name TEXT,
IN p_date_column TEXT,
IN p_days INTEGER
)
AS $$
DECLARE
v_sql TEXT;
BEGIN
— 构建清理SQL
v_sql := ‘DELETE FROM ‘ || quote_ident(p_table_name) || ‘
WHERE ‘ || quote_ident(p_date_column) || ‘ < NOW() - ' || p_days || ' * INTERVAL ''1 day'''; -- 执行清理 EXECUTE v_sql; RAISE NOTICE 'Cleaned up data in % older than % days', p_table_name, p_days; EXCEPTION WHEN OTHERS THEN RAISE EXCEPTION 'Error cleaning up data: %', SQLERRM; END; $$ LANGUAGE plpgsql; -- 输出结果 CREATE PROCEDURE -- 测试数据清理 CALL fgedu_cleanup_old_data('fgedu_orders', 'created_at', 365); -- 输出结果 NOTICE: Cleaned up data in fgedu_orders older than 365 days CALL

Part05-风哥经验总结与分享

5.1 PostgreSQL数据库最佳实践

最佳实践:

  • 函数设计:保持函数逻辑清晰,每个函数只做一件事
  • 存储过程设计:业务逻辑清晰,事务控制合理
  • 参数传递:使用适当的参数类型,避免类型转换
  • 错误处理:添加适当的异常处理机制
  • 性能优化:避免不必要的计算和查询
  • 安全性:使用参数化查询,避免SQL注入
  • 测试:充分测试函数和存储过程的各种场景
  • 文档:为函数和存储过程添加注释和文档
开发建议:

  • 使用命名规范,提高代码可读性
  • 使用版本控制管理函数和存储过程代码
  • 定期监控函数和存储过程的执行性能
  • 合理使用函数和存储过程,避免过度使用
  • 遵循PostgreSQL的编码规范

5.2 PostgreSQL数据库常见问题

常见问题:

  • 性能问题:函数执行效率低下
  • 内存问题:处理大量数据时内存不足
  • 错误处理:异常处理不完善
  • 安全问题:存在SQL注入风险
  • 维护问题:代码难以维护
  • 兼容性问题:不同PostgreSQL版本的语法差异

5.3 PostgreSQL数据库故障排查

故障排查:

  • 性能问题:使用EXPLAIN分析函数执行计划
  • 内存问题:检查数据量和处理逻辑
  • 错误处理:查看PostgreSQL日志,分析错误信息
  • 安全问题:检查SQL注入风险
  • 维护问题:重构代码,提高可读性
  • 兼容性问题:检查PostgreSQL版本,确保语法兼容
— 监控和管理示例

— 查看函数执行情况
SELECT
proname AS function_name,
calls AS call_count,
total_time AS total_execution_time,
mean_time AS average_execution_time
FROM pg_stat_user_functions
WHERE proname LIKE ‘fgedu%’;

— 输出结果
function_name | call_count | total_execution_time | average_execution_time
————————–+————+———————-+————————
fgedu_get_product_count | 2 | 0.234 | 0.117
fgedu_get_products_by_price_range | 1 | 0.123 | 0.123
fgedu_get_products_by_stock | 2 | 0.187 | 0.093
fgedu_get_employee_by_id | 1 | 0.098 | 0.098
fgedu_get_department_stats | 1 | 0.156 | 0.156
fgedu_adjust_salary | 1 | 0.112 | 0.112
fgedu_sales_analysis | 1 | 0.345 | 0.345
(7 rows)

— 查看函数依赖
SELECT
p.proname AS function_name,
d.refobjid::regclass AS dependency
FROM pg_proc p
JOIN pg_depend d ON d.objid = p.oid
WHERE p.proname LIKE ‘fgedu%’
AND d.refclassid = ‘pg_class’::regclass;

— 输出结果
function_name | dependency
————————–+——————-
fgedu_get_product_count | fgedu_products
fgedu_get_product_by_id | fgedu_products
fgedu_get_products_by_price_range | fgedu_products
fgedu_get_products_by_stock | fgedu_products
fgedu_manage_inventory | fgedu_products
fgedu_create_order | fgedu_orders
fgedu_create_order | fgedu_order_items
fgedu_create_order | fgedu_products
fgedu_process_batch_orders | fgedu_orders
fgedu_sync_data | fgedu_products
fgedu_sync_data | fgedu_products_backup
fgedu_cleanup_old_data | fgedu_orders
fgedu_get_employee_by_id | fgedu_employees
fgedu_get_department_stats | fgedu_employees
fgedu_adjust_salary | fgedu_employees
fgedu_sales_analysis | fgedu_orders
fgedu_sales_analysis | fgedu_order_items
fgedu_sales_analysis | fgedu_products
(19 rows)

风哥提示:自定义函数与存储过程是PostgreSQL的强大功能,可以用于实现复杂的业务逻辑。在使用过程中,需要注意:1) 保持代码逻辑清晰简洁;2) 充分测试性能和安全性;3) 建立完善的错误处理机制;4) 定期监控执行情况;5) 遵循最佳实践,提高代码质量。同时,要根据业务需求和性能要求,合理使用函数和存储过程,确保系统的高效运行。

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息