1. 首页 > PostgreSQL教程 > 正文

PostgreSQL教程FG151-PG扩展SQL:自定义函数与操作符

本文档风哥主要介绍PostgreSQL数据库扩展SQL的方法,包括自定义函数和操作符的创建、使用和优化,风哥教程参考PostgreSQL官方文档Server Programming和SQL Language内容,适合开发人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。

Part01-基础概念与理论知识

1.1 PostgreSQL数据库自定义函数概念

PostgreSQL数据库自定义函数是用户根据业务需求创建的函数,可以使用多种过程化语言编写,如PL/pgSQL、PL/Tcl、PL/Perl、PL/Python等。更多视频教程www.fgedu.net.cn

PostgreSQL数据库自定义函数的核心概念:

  • 函数签名:函数名、参数类型和返回类型
  • 函数体:函数的实现代码
  • 语言:编写函数使用的过程化语言
  • 参数:函数接受的输入参数
  • 返回值:函数返回的结果
  • 副作用:函数对数据库的修改操作

1.2 PostgreSQL数据库自定义操作符概念

PostgreSQL数据库自定义操作符是用户定义的运算符,可以为特定的数据类型实现自定义的操作逻辑。

# PostgreSQL自定义操作符

## 1. 操作符组成
– 操作符符号:如 +, -, *, /, @, # 等
– 操作数类型:操作符左侧和右侧的参数类型
– 结果类型:操作符返回的结果类型
– 实现函数:执行操作的函数
– 优先级:操作符的执行优先级
– 结合性:操作符的结合方向(左结合或右结合)

## 2. 操作符分类
– 一元操作符:只接受一个操作数
– 二元操作符:接受两个操作数
– 三元操作符:接受三个操作数(PostgreSQL不直接支持)

## 3. 操作符重载
– 可以为不同的数据类型定义相同的操作符符号
– PostgreSQL会根据操作数类型选择合适的操作符实现

1.3 PostgreSQL数据库扩展SQL的优势

PostgreSQL数据库扩展SQL的优势包括:

# 扩展SQL的优势

## 1. 业务逻辑封装
– 将复杂的业务逻辑封装为函数
– 提高代码复用性
– 简化SQL语句
– 便于维护和更新

## 2. 性能优化
– 减少网络传输开销
– 利用数据库服务器的计算能力
– 减少应用程序的负担
– 优化查询执行计划

## 3. 类型系统扩展
– 为自定义数据类型定义操作符
– 实现类型之间的转换
– 增强数据库的表达能力
– 提供更自然的查询语法

## 4. 功能扩展
– 实现数据库本身不支持的功能
– 集成外部系统和服务
– 扩展SQL的表达能力
– 满足特定业务需求

## 5. 安全性
– 控制数据访问权限
– 实现数据验证和约束
– 防止SQL注入攻击
– 增强数据安全性

Part02-生产环境规划与建议

2.1 PostgreSQL数据库自定义函数规划

生产环境中,自定义函数规划建议:

# 自定义函数规划

## 1. 函数设计
– 函数命名:使用有意义的名称,遵循命名规范
– 参数设计:合理设置参数类型和默认值
– 返回值设计:明确返回类型和语义
– 错误处理:实现完善的错误处理机制

## 2. 性能考虑
– 函数复杂度:避免过于复杂的函数
– 执行计划:考虑函数对查询计划的影响
– 缓存策略:合理使用函数缓存
– 资源消耗:监控函数的CPU和内存使用

## 3. 维护性
– 代码组织:模块化设计,便于维护
– 文档:为函数编写详细的文档
– 版本控制:使用版本控制系统管理函数代码
– 测试:编写单元测试和集成测试

## 4. 安全性
– 权限控制:合理设置函数的执行权限
– 输入验证:验证函数参数的有效性
– 防止SQL注入:使用参数化查询
– 敏感数据处理:保护敏感数据

## 5. 部署策略
– 部署时机:在应用部署前测试函数
– 版本管理:跟踪函数的版本变化
– 回滚机制:准备函数的回滚方案
– 监控:监控函数的执行情况

2.2 PostgreSQL数据库自定义操作符规划

自定义操作符规划建议:

# 自定义操作符规划

## 1. 操作符设计
– 操作符选择:选择合适的操作符符号
– 操作数类型:明确操作数的类型
– 结果类型:明确返回的结果类型
– 实现函数:选择或创建合适的实现函数

## 2. 优先级和结合性
– 优先级设置:根据操作符的语义设置合适的优先级
– 结合性设置:根据操作符的语义设置合适的结合性
– 冲突处理:避免操作符冲突
– 一致性:保持操作符行为的一致性

## 3. 性能考虑
– 实现效率:优化操作符的实现函数
– 索引支持:考虑为操作符创建索引支持
– 执行计划:考虑操作符对查询计划的影响
– 缓存策略:合理使用操作符缓存

## 4. 维护性
– 文档:为操作符编写详细的文档
– 版本控制:跟踪操作符的版本变化
– 测试:测试操作符的各种使用场景
– 兼容性:考虑操作符的兼容性

## 5. 安全性
– 输入验证:验证操作数的有效性
– 错误处理:实现完善的错误处理机制
– 权限控制:合理设置操作符的使用权限
– 边界情况:处理边界情况和异常输入

2.3 PostgreSQL数据库扩展SQL最佳实践

扩展SQL的最佳实践:

风哥教程针对风哥教程针对风哥教程针对生产环境建议:在生产环境中使用扩展SQL时,建议遵循以下最佳实践:

  • 优先使用PL/pgSQL编写函数,因为它与PostgreSQL的集成最好
  • 避免在函数中使用过多的业务逻辑,保持函数的单一职责
  • 为自定义操作符提供清晰的文档和使用示例
  • 定期测试和优化扩展SQL的性能
  • 使用版本控制系统管理扩展SQL的代码

学习交流加群风哥微信: itpux-com

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

3.1 PostgreSQL数据库自定义函数实现

# 自定义函数实现

## 1. PL/pgSQL函数

### 1.1 创建简单函数

“`sql
CREATE OR REPLACE FUNCTION fgedu_add(a INTEGER, b INTEGER)
RETURNS INTEGER AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql;
“`

### 1.2 创建带默认参数的函数

“`sql
CREATE OR REPLACE FUNCTION fgedu_greet(name VARCHAR DEFAULT ‘World’)
RETURNS VARCHAR AS $$
BEGIN
RETURN ‘Hello, ‘ || name || ‘!’;
END;
$$ LANGUAGE plpgsql;
“`

### 1.3 创建带异常处理的函数

“`sql
CREATE OR REPLACE FUNCTION fgedu_divide(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
IF b = 0 THEN
RAISE EXCEPTION ‘Division by zero’;
END IF;
RETURN a / b;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION ‘Error: %’, SQLERRM;
END;
$$ LANGUAGE plpgsql;
“`

### 1.4 创建返回表的函数

“`sql
CREATE OR REPLACE FUNCTION fgedu_get_students()
RETURNS TABLE(id INTEGER, name VARCHAR, age INTEGER)
AS $$
BEGIN
RETURN QUERY
SELECT s.id, s.name, s.age
FROM fgedu_student s;
END;
$$ LANGUAGE plpgsql;
“`

## 2. SQL函数

### 2.1 创建简单SQL函数

“`sql
CREATE OR REPLACE FUNCTION fgedu_square(x INTEGER)
RETURNS INTEGER AS $$
SELECT x * x;
$$ LANGUAGE sql;
“`

### 2.2 创建带参数的SQL函数

“`sql
CREATE OR REPLACE FUNCTION fgedu_calculate_discount(price NUMERIC, discount_rate NUMERIC)
RETURNS NUMERIC AS $$
SELECT price * (1 – discount_rate / 100);
$$ LANGUAGE sql;
“`

## 3. 其他语言函数

### 3.1 PL/Python函数

“`sql
CREATE OR REPLACE FUNCTION fgedu_python_upper(text VARCHAR)
RETURNS VARCHAR AS $$
return text.upper()
$$ LANGUAGE plpython3u;
“`

### 3.2 PL/Perl函数

“`sql
CREATE OR REPLACE FUNCTION fgedu_perl_upper(text VARCHAR)
RETURNS VARCHAR AS $$
return uc($_[0]);
$$ LANGUAGE plperl;
“`

3.2 PostgreSQL数据库自定义操作符实现

# 自定义操作符实现

## 1. 创建操作符

### 1.1 创建简单操作符

“`sql
— 首先创建实现函数
CREATE OR REPLACE FUNCTION fgedu_cube(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
RETURN n * n * n;
END;
$$ LANGUAGE plpgsql;

— 创建操作符
CREATE OPERATOR ^# (
PROCEDURE = fgedu_cube,
LEFTARG = INTEGER,
RIGHTARG = NONE
);
“`

### 1.2 创建二元操作符

“`sql
— 首先创建实现函数
CREATE OR REPLACE FUNCTION fgedu_power(base INTEGER, exponent INTEGER)
RETURNS INTEGER AS $$
DECLARE
result INTEGER := 1;
i INTEGER;
BEGIN
FOR i IN 1..exponent LOOP
result := result * base;
END LOOP;
RETURN result;
END;
$$ LANGUAGE plpgsql;

— 创建操作符
CREATE OPERATOR ** (
PROCEDURE = fgedu_power,
LEFTARG = INTEGER,
RIGHTARG = INTEGER
);
“`

### 1.3 创建带优先级和结合性的操作符

“`sql
— 创建操作符,设置优先级为5,左结合
CREATE OPERATOR @@ (
PROCEDURE = fgedu_custom_operation,
LEFTARG = INTEGER,
RIGHTARG = INTEGER,
PRIORITY = 5,
ASSOCIATION = LEFT
);
“`

## 2. 操作符重载

### 2.1 为不同类型重载操作符

“`sql
— 为INTEGER类型创建操作符
CREATE OR REPLACE FUNCTION fgedu_add(integer, integer)
RETURNS integer AS $$
SELECT $1 + $2;
$$ LANGUAGE sql;

— 为NUMERIC类型创建操作符
CREATE OR REPLACE FUNCTION fgedu_add(numeric, numeric)
RETURNS numeric AS $$
SELECT $1 + $2;
$$ LANGUAGE sql;

— 创建操作符
CREATE OPERATOR +# (
PROCEDURE = fgedu_add,
LEFTARG = INTEGER,
RIGHTARG = INTEGER
);

CREATE OPERATOR +# (
PROCEDURE = fgedu_add,
LEFTARG = NUMERIC,
RIGHTARG = NUMERIC
);
“`

## 3. 操作符索引支持

### 3.1 创建操作符类

“`sql
— 创建操作符类
CREATE OPERATOR CLASS gist_point_ops
DEFAULT FOR TYPE point USING gist AS
OPERATOR 1 <-> (point, point),
OPERATOR 2 <@ (point, box), OPERATOR 3 @> (point, box),
OPERATOR 4 ~= (point, point),
FUNCTION 1 gist_point_compress(internal),
FUNCTION 2 gist_point_decompress(internal),
FUNCTION 3 gist_point_consistent(internal, point, integer, oid, internal),
FUNCTION 4 gist_point_union(internal, internal),
FUNCTION 5 gist_point_same(point, point, internal);
“`

3.3 PostgreSQL数据库扩展SQL集成

# 扩展SQL集成

## 1. 函数和操作符的管理

### 1.1 查看函数

“`sql
— 查看所有函数
\df

— 查看特定函数
\df fgedu_add

— 查看函数详细信息
SELECT * FROM pg_proc WHERE proname = ‘fgedu_add’;
“`

### 1.2 查看操作符

“`sql
— 查看所有操作符
\do

— 查看特定操作符
\do ^#

— 查看操作符详细信息
SELECT * FROM pg_operator WHERE oprname = ‘^#’;
“`

### 1.3 修改函数

“`sql
— 修改函数定义
CREATE OR REPLACE FUNCTION fgedu_add(a INTEGER, b INTEGER)
RETURNS INTEGER AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql;
“`

### 1.4 删除函数和操作符

“`sql
— 删除函数
DROP FUNCTION IF EXISTS fgedu_add(INTEGER, INTEGER);

— 删除操作符
DROP OPERATOR IF EXISTS ^# (INTEGER);
“`

## 2. 扩展SQL的使用

### 2.1 在SQL语句中使用函数

“`sql
— 在SELECT语句中使用
SELECT fgedu_add(1, 2);

— 在WHERE子句中使用
SELECT * FROM fgedu_student WHERE fgedu_age_check(age);

— 在INSERT语句中使用
INSERT INTO fgedu_student (name, age) VALUES (fgedu_normalize_name(‘John Doe’), 20);
“`

### 2.2 在SQL语句中使用操作符

“`sql
— 使用自定义操作符
SELECT 2 ^#; — 计算2的立方

SELECT 2 ** 3; — 计算2的3次方

SELECT 10 +# 5; — 使用重载的加法操作符
“`

## 3. 扩展SQL的性能优化

### 3.1 函数性能优化

“`sql
— 使用IMMUTABLE标记纯函数
CREATE OR REPLACE FUNCTION fgedu_square(x INTEGER)
RETURNS INTEGER AS $$
SELECT x * x;
$$ LANGUAGE sql IMMUTABLE;

— 使用STABLE标记稳定函数
CREATE OR REPLACE FUNCTION fgedu_get_current_time()
RETURNS TIMESTAMP AS $$
SELECT CURRENT_TIMESTAMP;
$$ LANGUAGE sql STABLE;

— 使用VOLATILE标记易变函数
CREATE OR REPLACE FUNCTION fgedu_generate_random()
RETURNS INTEGER AS $$
SELECT floor(random() * 100);
$$ LANGUAGE sql VOLATILE;
“`

### 3.2 操作符性能优化

“`sql
— 为操作符创建索引支持
CREATE INDEX idx_student_age ON fgedu_student USING btree (age);

— 优化操作符实现函数
CREATE OR REPLACE FUNCTION fgedu_fast_operation(a INTEGER, b INTEGER)
RETURNS INTEGER AS $$
BEGIN
— 优化的实现
RETURN a * b;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
“`

Part04-生产案例与实战讲解

4.1 PostgreSQL数据库自定义函数实战

#!/usr/bin/env python3
# custom_functions_practice.py
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: `http://www.fgedu.net.cn`

import psycopg2
from psycopg2 import pool

# 数据库连接参数
DB_PARAMS = {
“fgedu.net.cn”: “localfgedu.net.cn”,
“port”: 5432,
“fgedudb”: “fgedudb”,
“fgedu”: “fgedu”,
“password”: “fgedu_password”
}

# 创建连接池
postgres_pool = None

def init_pool():
global postgres_pool
postgres_pool = pool.ThreadedConnectionPool(
minconn=5,
maxconn=20,
**DB_PARAMS
)
print(“连接池初始化成功”)

def get_connection():
“””从连接池获取连接”””
return postgres_pool.getconn()

def release_connection(conn):
“””将连接归还到连接池”””
postgres_pool.putconn(conn)

def execute_query(query, params=None):
“””执行SQL查询”””
conn = None
try:
conn = get_connection()
with conn.cursor() as cur:
cur.execute(query, params)
if query.strip().upper().startswith(“SELECT”):
result = cur.fetchall()
conn.commit()
return result
else:
conn.commit()
return cur.rowcount
except Exception as e:
if conn:
conn.rollback()
print(f”执行查询失败: {e}”)
raise
finally:
if conn:
release_connection(conn)

def create_functions():
“””创建自定义函数”””
# 创建计算学生平均成绩的函数
avg_grade_function = “””
CREATE OR REPLACE FUNCTION fgedu_calculate_avg_grade(student_id INTEGER)
RETURNS NUMERIC AS $$
DECLARE
avg_grade NUMERIC;
BEGIN
SELECT AVG(grade) INTO avg_grade
FROM fgedu_enrollment
WHERE student_id = $1;

RETURN COALESCE(avg_grade, 0);
END;
$$ LANGUAGE plpgsql;
“””
execute_query(avg_grade_function)
print(“计算学生平均成绩的函数创建成功”)

# 创建统计课程选课人数的函数
course_count_function = “””
CREATE OR REPLACE FUNCTION fgedu_count_course_enrollment(course_id INTEGER)
RETURNS INTEGER AS $$
DECLARE
count INTEGER;
BEGIN
SELECT COUNT(*) INTO count
FROM fgedu_enrollment
WHERE course_id = $1;

RETURN count;
END;
$$ LANGUAGE plpgsql;
“””
execute_query(course_count_function)
print(“统计课程选课人数的函数创建成功”)

# 创建检查学生年龄的函数
age_check_function = “””
CREATE OR REPLACE FUNCTION fgedu_age_check(age INTEGER)
RETURNS BOOLEAN AS $$
BEGIN
RETURN age >= 18 AND age <= 30; END; $$ LANGUAGE plpgsql; """ execute_query(age_check_function) print("检查学生年龄的函数创建成功") # 创建格式化学生信息的函数 format_student_function = """ CREATE OR REPLACE FUNCTION fgedu_format_student_info(id INTEGER) RETURNS VARCHAR AS $$ DECLARE student_record RECORD; BEGIN SELECT * INTO student_record FROM fgedu_student WHERE id = $1; IF NOT FOUND THEN RETURN 'Student not found'; END IF; RETURN student_record.name || ', ' || student_record.age || ' years old, ' || student_record.major; END; $$ LANGUAGE plpgsql; """ execute_query(format_student_function) print("格式化学生信息的函数创建成功") def test_functions(): """测试自定义函数""" # 测试计算学生平均成绩的函数 print("\n测试计算学生平均成绩的函数:") result = execute_query("SELECT fgedu_calculate_avg_grade(%s)", (1,)) print(f"学生1的平均成绩: {result[0][0]}") # 测试统计课程选课人数的函数 print("\n测试统计课程选课人数的函数:") result = execute_query("SELECT fgedu_count_course_enrollment(%s)", (1,)) print(f"课程1的选课人数: {result[0][0]}") # 测试检查学生年龄的函数 print("\n测试检查学生年龄的函数:") result = execute_query("SELECT fgedu_age_check(%s)", (20,)) print(f"年龄20是否有效: {result[0][0]}") result = execute_query("SELECT fgedu_age_check(%s)", (17,)) print(f"年龄17是否有效: {result[0][0]}") # 测试格式化学生信息的函数 print("\n测试格式化学生信息的函数:") result = execute_query("SELECT fgedu_format_student_info(%s)", (1,)) print(f"学生1的信息: {result[0][0]}") def main(): """主函数""" # 初始化连接池 init_pool() try: # 创建自定义函数 create_functions() # 测试自定义函数 test_functions() finally: # 关闭连接池 postgres_pool.closeall() print("\n连接池关闭成功") if __name__ == "__main__": main()

# 执行结果
$ python custom_functions_practice.py
连接池初始化成功
计算学生平均成绩的函数创建成功
统计课程选课人数的函数创建成功
检查学生年龄的函数创建成功
格式化学生信息的函数创建成功

测试计算学生平均成绩的函数:
学生1的平均成绩: 90.0

测试统计课程选课人数的函数:
课程1的选课人数: 2

测试检查学生年龄的函数:
年龄20是否有效: True
年龄17是否有效: False

测试格式化学生信息的函数:
学生1的信息: 风哥1号, 21 years old, 人工智能

连接池关闭成功

4.2 PostgreSQL数据库自定义操作符实战

#!/usr/bin/env python3
# custom_operators_practice.py
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: `http://www.fgedu.net.cn`

import psycopg2
from psycopg2 import pool

# 数据库连接参数
DB_PARAMS = {
“fgedu.net.cn”: “localfgedu.net.cn”,
“port”: 5432,
“fgedudb”: “fgedudb”,
“fgedu”: “fgedu”,
“password”: “fgedu_password”
}

# 创建连接池
postgres_pool = None

def init_pool():
global postgres_pool
postgres_pool = pool.ThreadedConnectionPool(
minconn=5,
maxconn=20,
**DB_PARAMS
)
print(“连接池初始化成功”)

def get_connection():
“””从连接池获取连接”””
return postgres_pool.getconn()

def release_connection(conn):
“””将连接归还到连接池”””
postgres_pool.putconn(conn)

def execute_query(query, params=None):
“””执行SQL查询”””
conn = None
try:
conn = get_connection()
with conn.cursor() as cur:
cur.execute(query, params)
if query.strip().upper().startswith(“SELECT”):
result = cur.fetchall()
conn.commit()
return result
else:
conn.commit()
return cur.rowcount
except Exception as e:
if conn:
conn.rollback()
print(f”执行查询失败: {e}”)
raise
finally:
if conn:
release_connection(conn)

def create_operators():
“””创建自定义操作符”””
# 创建立方操作符
cube_function = “””
CREATE OR REPLACE FUNCTION fgedu_cube(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
RETURN n * n * n;
END;
$$ LANGUAGE plpgsql;
“””
execute_query(cube_function)

cube_operator = “””
CREATE OPERATOR ^# (
PROCEDURE = fgedu_cube,
LEFTARG = INTEGER,
RIGHTARG = NONE
);
“””
execute_query(cube_operator)
print(“立方操作符创建成功”)

# 创建幂操作符
power_function = “””
CREATE OR REPLACE FUNCTION fgedu_power(base INTEGER, exponent INTEGER)
RETURNS INTEGER AS $$
DECLARE
result INTEGER := 1;
i INTEGER;
BEGIN
FOR i IN 1..exponent LOOP
result := result * base;
END LOOP;
RETURN result;
END;
$$ LANGUAGE plpgsql;
“””
execute_query(power_function)

power_operator = “””
CREATE OPERATOR ** (
PROCEDURE = fgedu_power,
LEFTARG = INTEGER,
RIGHTARG = INTEGER
);
“””
execute_query(power_operator)
print(“幂操作符创建成功”)

# 创建安全除法操作符
safe_divide_function = “””
CREATE OR REPLACE FUNCTION fgedu_safe_divide(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
IF b = 0 THEN
RETURN NULL;
END IF;
RETURN a / b;
END;
$$ LANGUAGE plpgsql;
“””
execute_query(safe_divide_function)

safe_divide_operator = “””
CREATE OPERATOR /# (
PROCEDURE = fgedu_safe_divide,
LEFTARG = NUMERIC,
RIGHTARG = NUMERIC
);
“””
execute_query(safe_divide_operator)
print(“安全除法操作符创建成功”)

def test_operators():
“””测试自定义操作符”””
# 测试立方操作符
print(“\n测试立方操作符:”)
result = execute_query(“SELECT 2 ^#”)
print(f”2的立方: {result[0][0]}”)
result = execute_query(“SELECT 3 ^#”)
print(f”3的立方: {result[0][0]}”)

# 测试幂操作符
print(“\n测试幂操作符:”)
result = execute_query(“SELECT 2 ** 3″)
print(f”2的3次方: {result[0][0]}”)
result = execute_query(“SELECT 3 ** 4″)
print(f”3的4次方: {result[0][0]}”)

# 测试安全除法操作符
print(“\n测试安全除法操作符:”)
result = execute_query(“SELECT 10 /# 2″)
print(f”10 /# 2: {result[0][0]}”)
result = execute_query(“SELECT 10 /# 0″)
print(f”10 /# 0: {result[0][0]}”)

def main():
“””主函数”””
# 初始化连接池
init_pool()

try:
# 创建自定义操作符
create_operators()

# 测试自定义操作符
test_operators()

finally:
# 关闭连接池
postgres_pool.closeall()
print(“\n连接池关闭成功”)

if __name__ == “__main__”:
main()

# 执行结果
$ python custom_operators_practice.py
连接池初始化成功
立方操作符创建成功
幂操作符创建成功
安全除法操作符创建成功

测试立方操作符:
2的立方: 8
3的立方: 27

测试幂操作符:
2的3次方: 8
3的4次方: 81

测试安全除法操作符:
10 /# 2: 5.0
10 /# 0: None

连接池关闭成功

4.3 PostgreSQL数据库扩展SQL案例分析

# 扩展SQL案例分析

## 1. 案例一:业务逻辑封装

### 1.1 需求
– 计算学生的GPA(平均学分绩点)
– 根据成绩等级计算绩点
– 考虑课程学分权重

### 1.2 实现

“`sql
— 创建计算绩点的函数
CREATE OR REPLACE FUNCTION fgedu_calculate_gpa(student_id INTEGER)
RETURNS NUMERIC AS $$
DECLARE
total_points NUMERIC := 0;
total_credits NUMERIC := 0;
rec RECORD;
BEGIN
— 计算总绩点和总学分
FOR rec IN
SELECT e.grade, c.credit
FROM fgedu_enrollment e
JOIN fgedu_course c ON e.course_id = c.id
WHERE e.student_id = student_id AND e.grade IS NOT NULL
LOOP
— 根据成绩计算绩点
CASE
WHEN rec.grade >= 90 THEN
total_points := total_points + (4.0 * rec.credit);
WHEN rec.grade >= 80 THEN
total_points := total_points + (3.0 * rec.credit);
WHEN rec.grade >= 70 THEN
total_points := total_points + (2.0 * rec.credit);
WHEN rec.grade >= 60 THEN
total_points := total_points + (1.0 * rec.credit);
ELSE
total_points := total_points + (0.0 * rec.credit);
END CASE;
total_credits := total_credits + rec.credit;
END LOOP;

— 计算GPA
IF total_credits > 0 THEN
RETURN total_points / total_credits;
ELSE
RETURN 0;
END IF;
END;
$$ LANGUAGE plpgsql;
“`

### 1.3 使用

“`sql
— 查询学生的GPA
SELECT fgedu_calculate_gpa(1);

— 按GPA排序学生
SELECT s.id, s.name, fgedu_calculate_gpa(s.id) AS gpa
FROM fgedu_student s
ORDER BY gpa DESC;
“`

## 2. 案例二:数据验证

### 2.1 需求
– 验证学生信息的有效性
– 确保年龄在合理范围内
– 确保专业名称符合规范

### 2.2 实现

“`sql
— 创建验证学生信息的函数
CREATE OR REPLACE FUNCTION fgedu_validate_student(name VARCHAR, age INTEGER, major VARCHAR)
RETURNS VARCHAR AS $$
BEGIN
— 验证姓名
IF name IS NULL OR name = ” THEN
RETURN ‘Name cannot be empty’;
END IF;

— 验证年龄
IF age < 16 OR age > 35 THEN
RETURN ‘Age must be between 16 and 35’;
END IF;

— 验证专业
IF major IS NULL OR major = ” THEN
RETURN ‘Major cannot be empty’;
END IF;

RETURN ‘Valid’;
END;
$$ LANGUAGE plpgsql;

— 创建插入学生的函数
CREATE OR REPLACE FUNCTION fgedu_insert_student(name VARCHAR, age INTEGER, gender VARCHAR, major VARCHAR)
RETURNS INTEGER AS $$
DECLARE
validation_result VARCHAR;
student_id INTEGER;
BEGIN
— 验证学生信息
validation_result := fgedu_validate_student(name, age, major);
IF validation_result <> ‘Valid’ THEN
RAISE EXCEPTION ‘Validation failed: %’, validation_result;
END IF;

— 插入学生
INSERT INTO fgedu_student (name, age, gender, major)
VALUES (name, age, gender, major)
RETURNING id INTO student_id;

RETURN student_id;
END;
$$ LANGUAGE plpgsql;
“`

### 2.3 使用

“`sql
— 插入学生
SELECT fgedu_insert_student(‘赵六’, 22, ‘男’, ‘计算机科学’);

— 尝试插入无效学生
SELECT fgedu_insert_student(‘钱七’, 15, ‘女’, ‘软件工程’); — 年龄无效
“`

## 3. 案例三:数据转换

### 3.1 需求
– 将JSON格式的学生信息转换为关系型数据
– 支持批量导入学生数据

### 3.2 实现

“`sql
— 创建从JSON导入学生的函数
CREATE OR REPLACE FUNCTION fgedu_import_students_from_json(json_data JSONB)
RETURNS INTEGER AS $$
DECLARE
item JSONB;
count INTEGER := 0;
BEGIN
— 遍历JSON数组
FOR item IN SELECT * FROM jsonb_array_elements(json_data) LOOP
— 插入学生
INSERT INTO fgedu_student (
name,
age,
gender,
major
) VALUES (
item->>’name’,
(item->>’age’)::INTEGER,
item->>’gender’,
item->>’major’
);
count := count + 1;
END LOOP;

RETURN count;
END;
$$ LANGUAGE plpgsql;
“`

### 3.3 使用

“`sql
— 导入学生数据
SELECT fgedu_import_students_from_json(‘[
{“name”: “孙八”, “age”: 20, “gender”: “男”, “major”: “数据科学”},
{“name”: “周九”, “age”: 21, “gender”: “女”, “major”: “人工智能”}
]’::jsonb);
“`

Part05-风哥经验总结与分享

5.1 PostgreSQL数据库自定义函数性能优化

PostgreSQL数据库自定义函数性能优化经验:

  • 使用合适的语言:对于简单函数,使用SQL语言;对于复杂逻辑,使用PL/pgSQL
  • 合理使用函数标记:根据函数的特性使用IMMUTABLE、STABLE或VOLATILE标记
  • 避免过度使用函数:对于简单操作,直接使用SQL语句可能更高效
  • 优化函数内部逻辑:减少函数内部的循环和复杂计算
  • 使用SQL语句代替PL/pgSQL:对于简单的数据操作,SQL函数通常比PL/pgSQL函数更高效
  • 避免在函数中使用事务:函数内部的事务会增加开销
  • 使用参数化查询:避免在函数中拼接SQL语句
  • 监控函数性能:使用pg_stat_fgedu_functions视图监控函数的执行情况
风哥提示:自定义函数是PostgreSQL的强大特性,但需要合理使用以避免性能问题。建议在开发过程中注意函数的复杂度和执行效率,定期监控和优化函数性能。学习交流加群风哥QQ113257174

5.2 PostgreSQL数据库自定义操作符性能优化

# 自定义操作符性能优化

## 1. 优化实现函数
– 使用高效的实现算法
– 减少函数内部的开销
– 避免不必要的计算
– 使用合适的语言编写实现函数

## 2. 索引支持
– 为操作符创建索引支持
– 使用合适的索引类型
– 优化索引的使用
– 定期维护索引

## 3. 操作符设计
– 选择合适的操作符符号
– 合理设置操作符的优先级和结合性
– 避免操作符冲突
– 保持操作符行为的一致性

## 4. 缓存策略
– 使用IMMUTABLE标记操作符的实现函数
– 利用PostgreSQL的查询缓存
– 避免在操作符中使用易变函数

## 5. 监控和调优
– 监控操作符的执行情况
– 分析操作符对查询计划的影响
– 优化操作符的使用方式
– 定期测试和调优操作符性能

5.3 PostgreSQL数据库扩展SQL问题排查

# 扩展SQL问题排查

## 1. 常见问题

### 1.1 函数执行错误
– 症状:函数执行时出现错误
– 原因:函数内部逻辑错误,参数类型不匹配,权限问题
– 解决方案:检查函数代码,验证参数类型,确保用户有执行权限

### 1.2 操作符冲突
– 症状:创建操作符时出现冲突错误
– 原因:操作符符号已被使用,参数类型组合已存在
– 解决方案:选择不同的操作符符号,或为不同的参数类型组合创建操作符

### 1.3 性能问题
– 症状:函数或操作符执行缓慢
– 原因:函数逻辑复杂,操作符实现效率低,缺少索引支持
– 解决方案:优化函数逻辑,改进操作符实现,添加索引支持

### 1.4 权限问题
– 症状:执行函数或操作符时出现权限错误
– 原因:用户没有执行权限,函数访问了无权限的对象
– 解决方案:授予用户执行权限,确保函数使用的对象有适当的权限

### 1.5 兼容性问题
– 症状:函数或操作符在不同版本的PostgreSQL中表现不同
– 原因:PostgreSQL版本差异,依赖的功能在不同版本中变化
– 解决方案:测试不同版本的兼容性,使用兼容的实现方式

## 2. 排查方法

### 2.1 错误日志分析
– 查看PostgreSQL错误日志
– 分析函数执行错误信息
– 定位错误发生的位置

### 2.2 性能分析
– 使用EXPLAIN分析使用函数或操作符的查询
– 监控函数的执行时间和调用次数
– 分析操作符对查询计划的影响

### 2.3 代码审查
– 审查函数和操作符的实现代码
– 检查参数类型和返回类型
– 验证错误处理逻辑

### 2.4 测试
– 编写单元测试测试函数和操作符
– 测试边界情况和异常输入
– 测试不同参数类型和值

### 2.5 权限检查
– 检查用户的执行权限
– 验证函数访问的对象权限
– 确保函数的安全定义

## 3. 解决方案

### 3.1 函数执行错误
– 修复函数内部逻辑
– 确保参数类型匹配
– 授予用户执行权限

### 3.2 操作符冲突
– 选择不同的操作符符号
– 为不同的参数类型组合创建操作符
– 检查现有的操作符定义

### 3.3 性能问题
– 优化函数逻辑
– 改进操作符实现
– 添加索引支持
– 使用更高效的语言编写函数

### 3.4 权限问题
– 授予用户执行权限
– 确保函数使用的对象有适当的权限
– 使用安全的函数定义

### 3.5 兼容性问题
– 测试不同版本的兼容性
– 使用兼容的实现方式
– 避免使用版本特定的功能

持续优化:扩展SQL是PostgreSQL的强大特性,通过合理使用自定义函数和操作符,可以显著提高数据库应用的开发效率和性能。建议在开发过程中不断学习和掌握扩展SQL的最佳实践,定期优化和维护自定义函数和操作符。更多学习教程公众号风哥教程itpux_com

风哥提示:扩展SQL是PostgreSQL的重要特性,掌握自定义函数和操作符的创建和使用,对于开发高质量的数据库应用至关重要。建议在实际项目中合理使用扩展SQL,提高代码的复用性和可维护性。from PostgreSQL视频:www.itpux.com

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

联系我们

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

微信号:itpux-com

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