#视图、函数和过程 - 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;
-- 查看视图信息
SELECT * FROM information_schema.views
WHERE table_name = 'active_earners';
-- 修改视图
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;
-- 可更新视图
-- 简单的视图通常是可更新的
UPDATE active_employees
SET salary = salary * 1.1
WHERE employee_id = 123;#MySQL视图算法:
-- 指定视图算法
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;
-- UNDEFINED(默认):由MySQL选择
-- MERGE:将视图查询合并到外部查询
-- TEMPTABLE:创建临时表存储视图结果#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;
-- 查看视图定义
\dv -- psql命令
SELECT table_name, view_definition
FROM information_schema.views
WHERE table_name = 'active_employees';
-- 安全视图(with local/security invoker)
CREATE VIEW secure_view AS
SELECT employee_id, first_name, last_name
FROM employees
WHERE current_user = 'admin'
WITH LOCAL CHECK OPTION;#1.4 视图最佳实践
-- 性能优化的视图
CREATE VIEW optimized_view AS
SELECT
e.employee_id,
e.first_name,
e.last_name,
d.department_name,
-- 预计算复杂表达式
CASE
WHEN e.salary > 100000 THEN 'High'
WHEN e.salary > 75000 THEN 'Medium'
ELSE 'Low'
END as salary_level
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.status = 'active';
-- 安全性视图(隐藏敏感信息)
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;
-- 复杂函数示例
DELIMITER //
CREATE FUNCTION get_employee_grade(salary DECIMAL(10,2))
RETURNS VARCHAR(10)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE grade VARCHAR(10);
CASE
WHEN salary >= 100000 THEN SET grade = 'Senior';
WHEN salary >= 75000 THEN SET grade = 'Mid';
WHEN salary >= 50000 THEN SET grade = 'Junior';
ELSE SET grade = 'Entry';
END CASE;
RETURN grade;
END //
DELIMITER ;
-- 字符串处理函数
DELIMITER //
CREATE FUNCTION format_full_name(first_name VARCHAR(50), last_name VARCHAR(50))
RETURNS VARCHAR(101)
DETERMINISTIC
BEGIN
RETURN CONCAT(TRIM(first_name), ' ', TRIM(last_name));
END //
DELIMITER ;#MySQL函数特性:
-- 查看函数
SHOW FUNCTION STATUS WHERE Name = 'calculate_bonus';
-- 查看函数定义
SHOW CREATE FUNCTION calculate_bonus;
-- 修改函数
DROP FUNCTION IF EXISTS calculate_bonus;
DELIMITER //
CREATE FUNCTION calculate_bonus(salary DECIMAL(10,2), bonus_rate DECIMAL(3,2))
RETURNS DECIMAL(10,2)
READS SQL DATA
DETERMINISTIC
COMMENT 'Calculate bonus based on salary and rate'
BEGIN
DECLARE bonus_amount DECIMAL(10,2);
SET bonus_amount = salary * bonus_rate;
RETURN bonus_amount;
END //
DELIMITER ;
-- 函数修饰符说明
-- DETERMINISTIC:相同输入总是产生相同输出
-- NOT DETERMINISTIC:(默认)输出可能因环境而异
-- READS SQL DATA:函数读取数据但不修改
-- MODIFIES SQL DATA:函数可能修改数据
-- CONTAINS SQL:函数包含SQL但不读写数据
-- SQL SECURITY DEFINER:以定义者权限执行
-- SQL SECURITY INVOKER:以调用者权限执行#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_employee_grade(salary NUMERIC)
RETURNS TEXT AS $$
DECLARE
grade TEXT;
BEGIN
CASE
WHEN salary >= 100000 THEN
grade := 'Senior';
WHEN salary >= 75000 THEN
grade := 'Mid';
WHEN salary >= 50000 THEN
grade := 'Junior';
ELSE
grade := 'Entry';
END CASE;
RETURN grade;
END;
$$ LANGUAGE plpgsql;
-- 返回表类型的函数
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);
-- 字符串处理函数
CREATE OR REPLACE FUNCTION format_full_name(first_name TEXT, last_name TEXT)
RETURNS TEXT AS $$
BEGIN
RETURN TRIM(first_name || ' ' || last_name);
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- IMMUTABLE表示函数在相同输入下总是返回相同结果,可以被优化#PostgreSQL高级函数特性:
-- 使用不同语言的函数
-- Python函数(需要plpythonu扩展)
CREATE EXTENSION IF NOT EXISTS plpython3u;
CREATE OR REPLACE FUNCTION python_string_process(input_text TEXT)
RETURNS TEXT AS $$
return input_text.upper().strip()
$$ LANGUAGE plpython3u;
-- 聚合函数
CREATE AGGREGATE geometric_mean(NUMERIC) (
SFUNC = float8mul,
STYPE = NUMERIC,
FINALFUNC = sqrt,
INITCOND = '1'
);
-- 窗口函数
CREATE OR REPLACE FUNCTION moving_average(state NUMERIC, value NUMERIC, window_size INTEGER)
RETURNS NUMERIC AS $$
BEGIN
-- 实现移动平均逻辑
RETURN state;
END;
$$ LANGUAGE plpgsql WINDOW;
-- 函数重载
CREATE OR REPLACE FUNCTION add_numbers(a INTEGER, b INTEGER)
RETURNS INTEGER AS $$ BEGIN RETURN a + b; END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION add_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$ BEGIN RETURN a + b; END; $$ LANGUAGE plpgsql;#2.4 函数性能考虑
-- MySQL函数性能优化
DELIMITER //
CREATE FUNCTION optimized_calculation(value1 DECIMAL(10,2), value2 DECIMAL(10,2))
RETURNS DECIMAL(10,2)
READS SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
-- 避免复杂计算
DECLARE result DECIMAL(10,2);
SET result = value1 + value2;
RETURN result;
END //
DELIMITER ;
-- PostgreSQL函数性能优化
CREATE OR REPLACE FUNCTION optimized_calculation(val1 NUMERIC, val2 NUMERIC)
RETURNS NUMERIC AS $$
SELECT val1 + val2; -- 直接SQL,性能更好
$$ LANGUAGE sql IMMUTABLE;
-- 使用内联函数(PostgreSQL 14+)
CREATE OR REPLACE FUNCTION simple_calculation(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
SELECT a * b + 100;
$$ LANGUAGE sql IMMUTABLE PARALLEL SAFE;#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);
-- 带输出参数的存储过程
DELIMITER //
CREATE PROCEDURE GetEmployeeStats(
OUT total_count INT,
OUT avg_salary DECIMAL(10,2),
OUT max_salary DECIMAL(10,2)
)
BEGIN
SELECT
COUNT(*),
AVG(salary),
MAX(salary)
INTO total_count, avg_salary, max_salary
FROM employees;
END //
DELIMITER ;
-- 调用带输出参数的过程
CALL GetEmployeeStats(@count, @avg_sal, @max_sal);
SELECT @count, @avg_sal, @max_sal;
-- 复杂存储过程示例
DELIMITER //
CREATE PROCEDURE ProcessMonthlyPayroll(IN month_year DATE)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE emp_id INT;
DECLARE emp_salary DECIMAL(10,2);
DECLARE emp_bonus DECIMAL(10,2);
DECLARE cur CURSOR FOR
SELECT employee_id, salary
FROM employees
WHERE status = 'active';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- 创建临时表存储结果
CREATE TEMPORARY TABLE temp_payroll (
employee_id INT,
base_salary DECIMAL(10,2),
bonus DECIMAL(10,2),
total_pay DECIMAL(10,2)
);
OPEN cur;
read_loop: LOOP
FETCH cur INTO emp_id, emp_salary;
IF done THEN
LEAVE read_loop;
END IF;
-- 计算奖金(示例逻辑)
SET emp_bonus = emp_salary * 0.1;
-- 插入临时表
INSERT INTO temp_payroll
VALUES (emp_id, emp_salary, emp_bonus, emp_salary + emp_bonus);
END LOOP;
CLOSE cur;
-- 返回结果
SELECT * FROM temp_payroll;
-- 清理
DROP TEMPORARY TABLE temp_payroll;
END //
DELIMITER ;
-- 条件逻辑存储过程
DELIMITER //
CREATE PROCEDURE UpdateEmployeeSalary(
IN emp_id INT,
IN new_salary DECIMAL(10,2),
OUT result_message VARCHAR(255)
)
BEGIN
DECLARE current_salary DECIMAL(10,2);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SET result_message = 'Error occurred during salary update';
END;
START TRANSACTION;
-- 检查员工是否存在
SELECT salary INTO current_salary
FROM employees
WHERE employee_id = emp_id;
IF current_salary IS NULL THEN
SET result_message = 'Employee not found';
ROLLBACK;
ELSE
-- 更新薪资
UPDATE employees
SET salary = new_salary,
updated_at = NOW()
WHERE employee_id = emp_id;
SET result_message = CONCAT('Salary updated from ', current_salary, ' to ', new_salary);
COMMIT;
END IF;
END //
DELIMITER ;#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;
result_message TEXT;
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);
-- 带事务控制的存储过程
CREATE OR REPLACE PROCEDURE process_monthly_payroll(
month_year DATE
)
LANGUAGE plpgsql
AS $$
DECLARE
emp_record RECORD;
emp_bonus NUMERIC;
BEGIN
-- 开始事务
BEGIN
-- 遍历活跃员工
FOR emp_record IN
SELECT employee_id, salary
FROM employees
WHERE status = 'active'
LOOP
-- 计算奖金
emp_bonus := emp_record.salary * 0.1;
-- 插入薪资记录
INSERT INTO payroll_records (
employee_id,
pay_period,
base_salary,
bonus,
total_pay
) VALUES (
emp_record.employee_id,
month_year,
emp_record.salary,
emp_bonus,
emp_record.salary + emp_bonus
);
END LOOP;
-- 提交事务
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- 回滚事务
ROLLBACK;
RAISE;
END;
END;
$$;
-- 使用游标的存储过程
CREATE OR REPLACE PROCEDURE process_employees_batch(
batch_size INTEGER DEFAULT 100
)
LANGUAGE plpgsql
AS $$
DECLARE
emp_cursor CURSOR FOR
SELECT employee_id, first_name, last_name
FROM employees
WHERE status = 'active'
ORDER BY employee_id;
emp_record RECORD;
counter INTEGER := 0;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO emp_record;
EXIT WHEN NOT FOUND;
-- 处理员工记录
UPDATE employees
SET last_processed = CURRENT_TIMESTAMP
WHERE employee_id = emp_record.employee_id;
counter := counter + 1;
-- 批量提交
IF counter % batch_size = 0 THEN
COMMIT;
END IF;
END LOOP;
CLOSE emp_cursor;
-- 最终提交剩余记录
COMMIT;
RAISE NOTICE 'Processed % employee records', counter;
END;
$$;#3.4 存储过程最佳实践
-- MySQL最佳实践
DELIMITER //
CREATE PROCEDURE SafeEmployeeUpdate(
IN p_employee_id INT,
IN p_first_name VARCHAR(50),
IN p_last_name VARCHAR(50),
IN p_email VARCHAR(100),
OUT p_result VARCHAR(255)
)
MODIFIES SQL DATA
SQL SECURITY DEFINER
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
GET DIAGNOSTICS CONDITION 1
p_result = MESSAGE_TEXT;
END;
START TRANSACTION;
-- 输入验证
IF p_first_name IS NULL OR LENGTH(TRIM(p_first_name)) = 0 THEN
SET p_result = 'First name cannot be empty';
ROLLBACK;
LEAVE proc_label;
END IF;
-- 更新员工信息
UPDATE employees
SET
first_name = p_first_name,
last_name = p_last_name,
email = p_email,
updated_at = NOW()
WHERE employee_id = p_employee_id;
IF ROW_COUNT() = 0 THEN
SET p_result = 'No employee found with given ID';
ROLLBACK;
ELSE
SET p_result = 'Employee updated successfully';
COMMIT;
END IF;
proc_label: BEGIN END;
END //
DELIMITER ;
-- PostgreSQL最佳实践
CREATE OR REPLACE PROCEDURE validate_and_update_employee(
p_employee_id INTEGER,
p_first_name TEXT,
p_last_name TEXT,
p_email TEXT
)
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
v_old_email TEXT;
v_result TEXT;
BEGIN
-- 检查输入参数
IF p_first_name IS NULL OR LENGTH(TRIM(p_first_name)) = 0 THEN
RAISE EXCEPTION 'First name cannot be empty';
END IF;
-- 记录原始邮箱用于审计
SELECT email INTO v_old_email
FROM employees
WHERE employee_id = p_employee_id;
-- 更新员工信息
UPDATE employees
SET
first_name = TRIM(p_first_name),
last_name = TRIM(p_last_name),
email = LOWER(TRIM(p_email)),
updated_at = CURRENT_TIMESTAMP
WHERE employee_id = p_employee_id;
-- 检查是否成功更新
IF NOT FOUND THEN
RAISE EXCEPTION 'Employee with ID % not found', p_employee_id;
END IF;
-- 记录变更日志
INSERT INTO employee_audit_log (
employee_id,
field_changed,
old_value,
new_value,
changed_at,
changed_by
) VALUES (
p_employee_id,
'EMAIL',
v_old_email,
LOWER(TRIM(p_email)),
CURRENT_TIMESTAMP,
CURRENT_USER
);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
$$;#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_first_name,
new_first_name,
old_last_name,
new_last_name,
old_salary,
new_salary,
change_date,
changed_by
) VALUES (
NEW.employee_id,
OLD.first_name,
NEW.first_name,
OLD.last_name,
NEW.last_name,
OLD.salary,
NEW.salary,
NOW(),
USER()
);
END //
DELIMITER ;
-- 删除触发器
DROP TRIGGER IF EXISTS employee_audit_trigger;
-- BEFORE触发器示例
DELIMITER //
CREATE TRIGGER employee_validation_trigger
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
-- 验证薪资范围
IF NEW.salary < 30000 THEN
SET NEW.salary = 30000;
END IF;
-- 格式化邮箱
SET NEW.email = LOWER(TRIM(NEW.email));
-- 设置默认入职日期
IF NEW.hire_date IS NULL THEN
SET NEW.hire_date = CURDATE();
END IF;
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_first_name,
new_first_name,
old_last_name,
new_last_name,
old_salary,
new_salary,
change_date,
changed_by
) VALUES (
NEW.employee_id,
OLD.first_name,
NEW.first_name,
OLD.last_name,
NEW.last_name,
OLD.salary,
NEW.salary,
CURRENT_TIMESTAMP,
CURRENT_USER
);
RETURN NEW;
-- 处理插入操作
ELSIF TG_OP = 'INSERT' THEN
INSERT INTO employee_audit (
employee_id,
new_first_name,
new_last_name,
new_salary,
change_date,
changed_by,
operation
) VALUES (
NEW.employee_id,
NEW.first_name,
NEW.last_name,
NEW.salary,
CURRENT_TIMESTAMP,
CURRENT_USER,
'INSERT'
);
RETURN NEW;
-- 处理删除操作
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO employee_audit (
employee_id,
old_first_name,
old_last_name,
old_salary,
change_date,
changed_by,
operation
) VALUES (
OLD.employee_id,
OLD.first_name,
OLD.last_name,
OLD.salary,
CURRENT_TIMESTAMP,
CURRENT_USER,
'DELETE'
);
RETURN OLD;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- 创建触发器
CREATE TRIGGER employee_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON employees
FOR EACH ROW
EXECUTE FUNCTION audit_employee_changes();
-- 删除触发器
DROP TRIGGER IF EXISTS employee_audit_trigger ON employees;
-- BEFORE触发器示例
CREATE OR REPLACE FUNCTION validate_employee_insert()
RETURNS TRIGGER AS $$
BEGIN
-- 验证薪资范围
IF NEW.salary < 30000 THEN
RAISE EXCEPTION 'Salary cannot be less than 30000, given: %', NEW.salary;
END IF;
-- 格式化邮箱
NEW.email := LOWER(TRIM(NEW.email));
-- 设置默认入职日期
IF NEW.hire_date IS NULL THEN
NEW.hire_date := CURRENT_DATE;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER employee_validation_trigger
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
EXECUTE FUNCTION validate_employee_insert();#5. MySQL与PostgreSQL实现差异总结
| 特性 | MySQL | PostgreSQL |
|---|---|---|
| 函数返回值 | 必须有RETURN语句 | 多种返回方式 |
| 存储过程 | 8.0+完整支持 | 11+支持,之前用函数模拟 |
| 触发器 | CREATE TRIGGER语法 | CREATE TRIGGER语法 |
| 游标支持 | 支持(过程内) | 支持(函数和过程) |
| 异常处理 | DECLARE HANDLER | EXCEPTION块 |
| 事务控制 | START/COMMIT/ROLLBACK | BEGIN/COMMIT/ROLLBACK |
| 语言支持 | 主要是SQL和C | PL/pgSQL, Python, Perl等 |
| 视图算法 | MERGE/TEMPTABLE/UNDEFINED | 规划器自动优化 |
| 物化视图 | 不支持 | 原生支持 |
#6. 性能优化和最佳实践
#6.1 性能优化建议
-- MySQL性能优化
-- 1. 避免在函数中进行复杂查询
-- 2. 使用适当的索引
-- 3. 限制结果集大小
-- 4. 避免递归调用
-- PostgreSQL性能优化
-- 1. 使用IMMUTABLE函数进行优化
-- 2. 合理使用并行处理
-- 3. 优化触发器逻辑
-- 4. 使用适当的数据类型
-- 通用优化技巧
-- 1. 预编译语句
-- 2. 批量处理
-- 3. 合理的错误处理
-- 4. 适当的日志记录#6.2 安全最佳实践
-- 使用适当的安全上下文
-- MySQL
SQL SECURITY INVOKER -- 以调用者权限执行
SQL SECURITY DEFINER -- 以定义者权限执行
-- PostgreSQL
SECURITY DEFINER
SECURITY INVOKER
-- 权限管理
-- 只授予必要的权限
GRANT EXECUTE ON FUNCTION calculate_bonus TO app_user;
GRANT EXECUTE ON PROCEDURE update_employee_salary TO app_user;#相关教程
#总结
视图、函数和存储过程是数据库编程的重要组成部分:
- 视图:提供数据抽象和安全层,简化复杂查询
- 函数:封装计算逻辑,返回单一值
- 存储过程:执行复杂业务逻辑,支持事务控制
- 触发器:自动执行特定操作,用于审计和数据完整性
在MySQL和PostgreSQL中,这些功能都有很好的支持,但实现细节有所不同。选择使用哪种数据库对象应基于具体的业务需求、性能要求和安全考虑。理解这些对象的特性和最佳实践对于构建高效、安全的数据库应用程序至关重要。

