#视图、函数和过程 - MySQL与PostgreSQL实现对比
#引言
数据库编程是现代应用开发的重要组成部分。视图、函数和存储过程等数据库对象,能帮我们封装复杂逻辑、提升效率、增强安全性,并简化应用代码。本文将详细介绍这些对象的创建与使用,对比MySQL和PostgreSQL的实现差异。
#1. 视图(Views)
#1.1 什么是视图?
视图是一种虚拟表,其内容由查询定义。与真实表不同,视图不存储数据,而是在引用时动态生成结果集。
#1.2 MySQL视图
#基础视图创建:
-- 简单视图
CREATE VIEW active_employees AS
SELECT employee_id, first_name, last_name, email, salary
FROM employees
WHERE status = 'active';
-- 复杂视图
CREATE VIEW department_summary AS
SELECT
d.department_id,
d.department_name,
COUNT(e.employee_id) as employee_count,
AVG(e.salary) as avg_salary,
MIN(e.hire_date) as earliest_hire,
MAX(e.salary) as max_salary
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;
-- 带检查选项的视图
CREATE VIEW high_earners AS
SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE salary > 75000
WITH CHECK OPTION; -- 确保通过视图修改的数据满足WHERE条件#MySQL视图特性:
-- 查看视图定义
SHOW CREATE VIEW active_employees;
-- 修改视图
CREATE OR REPLACE VIEW active_employees AS
SELECT employee_id, first_name, last_name, email, salary, hire_date
FROM employees
WHERE status = 'active';
-- 删除视图
DROP VIEW IF EXISTS active_employees;
-- 指定视图算法
CREATE ALGORITHM=MERGE VIEW emp_dept_view AS
SELECT e.employee_id, e.first_name, e.last_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;#1.3 PostgreSQL视图
#PostgreSQL视图创建:
-- 简单视图
CREATE VIEW active_employees AS
SELECT employee_id, first_name, last_name, email, salary
FROM employees
WHERE status = 'active';
-- 复杂视图
CREATE VIEW department_summary AS
SELECT
d.department_id,
d.department_name,
COUNT(e.employee_id) as employee_count,
AVG(e.salary) as avg_salary,
MIN(e.hire_date) as earliest_hire,
MAX(e.salary) as max_salary
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;
-- 带检查选项的视图
CREATE VIEW high_earners AS
SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE salary > 75000
WITH CHECK OPTION;#PostgreSQL高级视图特性:
-- 物化视图(PostgreSQL特有)
CREATE MATERIALIZED VIEW monthly_sales_summary AS
SELECT
DATE_TRUNC('month', sale_date) as month,
SUM(amount) as total_sales,
COUNT(*) as transaction_count
FROM sales
GROUP BY DATE_TRUNC('month', sale_date);
-- 刷新物化视图
REFRESH MATERIALIZED VIEW monthly_sales_summary;
-- 并发刷新(不锁定表)
REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_sales_summary;#1.4 视图最佳实践
-- 安全性视图(隐藏敏感信息)
CREATE VIEW employee_public_view AS
SELECT
employee_id,
first_name,
last_name,
department_id,
hire_date
FROM employees
WHERE status = 'active';
-- 使用视图进行数据抽象
CREATE VIEW customer_order_summary AS
SELECT
c.customer_id,
c.customer_name,
COUNT(o.order_id) as total_orders,
SUM(o.total_amount) as total_spent,
MAX(o.order_date) as last_order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name;#2. 存储函数(Functions)
#2.1 什么是存储函数?
存储函数是数据库中的预编译代码块,可接收参数、执行操作并返回单个值。
#2.2 MySQL存储函数
-- 简单函数
DELIMITER //
CREATE FUNCTION calculate_bonus(salary DECIMAL(10,2), bonus_rate DECIMAL(3,2))
RETURNS DECIMAL(10,2)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE bonus_amount DECIMAL(10,2);
SET bonus_amount = salary * bonus_rate;
RETURN bonus_amount;
END //
DELIMITER ;
-- 使用函数
SELECT employee_id, first_name, last_name,
salary,
calculate_bonus(salary, 0.1) as bonus
FROM employees;#2.3 PostgreSQL存储函数
-- 使用PL/pgSQL语言的函数
CREATE OR REPLACE FUNCTION calculate_bonus(salary NUMERIC, bonus_rate NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
RETURN salary * bonus_rate;
END;
$$ LANGUAGE plpgsql;
-- 使用函数
SELECT employee_id, first_name, last_name,
salary,
calculate_bonus(salary, 0.1) as bonus
FROM employees;
-- 返回表类型的函数
CREATE OR REPLACE FUNCTION get_high_earners(min_salary NUMERIC)
RETURNS TABLE (
employee_id INTEGER,
first_name TEXT,
last_name TEXT,
salary NUMERIC
) AS $$
BEGIN
RETURN QUERY
SELECT e.employee_id, e.first_name, e.last_name, e.salary
FROM employees e
WHERE e.salary >= min_salary
ORDER BY e.salary DESC;
END;
$$ LANGUAGE plpgsql;
-- 使用返回表的函数
SELECT * FROM get_high_earners(80000);#3. 存储过程(Stored Procedures)
#3.1 什么是存储过程?
存储过程是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程名并给出参数来执行它。
#3.2 MySQL存储过程
-- 简单存储过程
DELIMITER //
CREATE PROCEDURE GetEmployeeById(IN emp_id INT)
BEGIN
SELECT employee_id, first_name, last_name, email, salary
FROM employees
WHERE employee_id = emp_id;
END //
DELIMITER ;
-- 调用存储过程
CALL GetEmployeeById(123);#3.3 PostgreSQL存储过程
-- PostgreSQL 11+开始支持真正的存储过程
CREATE OR REPLACE PROCEDURE update_employee_salary(
emp_id INTEGER,
new_salary NUMERIC
)
LANGUAGE plpgsql
AS $$
DECLARE
current_salary NUMERIC;
BEGIN
-- 检查员工是否存在
SELECT salary INTO current_salary
FROM employees
WHERE employee_id = emp_id;
IF current_salary IS NULL THEN
RAISE NOTICE 'Employee not found';
RETURN;
END IF;
-- 更新薪资
UPDATE employees
SET salary = new_salary,
updated_at = NOW()
WHERE employee_id = emp_id;
RAISE NOTICE 'Salary updated from % to %', current_salary, new_salary;
END;
$$;
-- 调用存储过程
CALL update_employee_salary(123, 75000);#4. 触发器(Triggers)
#4.1 MySQL触发器
-- 创建审计触发器
DELIMITER //
CREATE TRIGGER employee_audit_trigger
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
INSERT INTO employee_audit (
employee_id,
old_salary,
new_salary,
change_date,
changed_by
) VALUES (
NEW.employee_id,
OLD.salary,
NEW.salary,
NOW(),
USER()
);
END //
DELIMITER ;#4.2 PostgreSQL触发器
-- 创建触发器函数
CREATE OR REPLACE FUNCTION audit_employee_changes()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'UPDATE' THEN
INSERT INTO employee_audit (
employee_id,
old_salary,
new_salary,
change_date,
changed_by
) VALUES (
NEW.employee_id,
OLD.salary,
NEW.salary,
CURRENT_TIMESTAMP,
CURRENT_USER
);
RETURN NEW;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- 创建触发器
CREATE TRIGGER employee_audit_trigger
AFTER UPDATE ON employees
FOR EACH ROW
EXECUTE FUNCTION audit_employee_changes();#5. MySQL与PostgreSQL实现差异总结
| 特性 | MySQL | PostgreSQL |
|---|---|---|
| 函数返回值 | 必须有RETURN语句 | 多种返回方式 |
| 存储过程 | 8.0+完整支持 | 11+支持,之前用函数模拟 |
| 触发器 | 直接绑定逻辑 | 需要先创建函数 |
| 语言支持 | 主要是SQL和C | PL/pgSQL, Python, Perl等 |
| 物化视图 | 不支持 | 原生支持 |
#相关教程
#总结
视图、函数和存储过程是数据库编程的重要组成部分:
- 视图:提供数据抽象和安全层,简化复杂查询
- 函数:封装计算逻辑,返回单一值
- 存储过程:执行复杂业务逻辑,支持事务控制
- 触发器:自动执行特定操作,用于审计和数据完整性
在MySQL和PostgreSQL中,这些功能都有很好的支持,但实现细节有所不同。选择使用哪种数据库对象应基于具体的业务需求、性能要求和安全考虑。

