PostgreSQL教程FG166-PL/Python基础:Python语言在PG中的使用
本文档风哥主要介绍PostgreSQL数据库中PL/Python的基础知识,包括Python语言在PostgreSQL中的使用方法,风哥教程参考PostgreSQL官方文档PL/Python内容,适合数据库开发人员和DBA在生产环境中使用Python编写数据库函数。
Part01-基础概念与理论知识
1.1 PostgreSQL数据库PL/Python概念
PL/Python是PostgreSQL的过程化编程语言,允许在数据库中使用Python语言编写函数和存储过程。更多视频教程www.fgedu.net.cn。
- 使用Python语言编写数据库函数
- 支持Python的所有特性
- 可以访问PostgreSQL数据库
- 可以使用Python的第三方库
- 与PostgreSQL的其他过程化语言相比,Python更加灵活和强大
1.2 PostgreSQL数据库PL/Python特性
PL/Python的主要特性包括:
- 动态类型:Python是动态类型语言,不需要显式声明变量类型
- 丰富的库:可以使用Python的标准库和第三方库
- 简洁的语法:Python语法简洁易读
- 面向对象:支持面向对象编程
- 异常处理:支持异常处理机制
1.3 PostgreSQL数据库PL/Python安装
PL/Python是PostgreSQL的扩展模块,需要在编译PostgreSQL时启用或作为扩展安装。学习交流加群风哥微信: itpux-com。
SELECT * FROM pg_available_extensions WHERE name LIKE ‘plpython%’;
— 输出结果
name | default_version | installed_version | comment
——————+—————-+——————-+———————————————————–
plpython3u | 1.0 | 1.0 | PL/Python3U untrusted procedural language
— 安装PL/Python扩展
CREATE EXTENSION IF NOT EXISTS plpython3u;
— 输出结果
CREATE EXTENSION
— 验证安装
SELECT extname, extversion FROM pg_extension WHERE extname LIKE ‘plpython%’;
— 输出结果
extname | extversion
————+————
plpython3u | 1.0
(1 row)
Part02-生产环境规划与建议
2.1 PostgreSQL数据库PL/Python设计原则
PL/Python设计原则:单一职责,每个函数只做一件事;模块化设计,便于维护;性能优先,避免复杂逻辑;安全性,避免SQL注入;可读性,编写清晰的代码。
2.2 PostgreSQL数据库PL/Python性能考虑
性能考虑:避免频繁的数据库交互;使用批量操作;合理使用缓存;避免在函数中执行大量计算;监控函数执行性能;定期优化代码。
2.3 PostgreSQL数据库PL/Python安全考虑
安全考虑:避免SQL注入;使用参数化查询;限制函数权限;验证输入参数;避免特权操作;定期更新Python依赖库。
Part03-生产环境项目实施方案
3.1 PostgreSQL数据库PL/Python函数创建
3.1.1 函数创建
— 创建测试表
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
);
— 输出结果
CREATE TABLE
— 插入测试数据
INSERT INTO fgedu_employees(name, department, salary)
VALUES
(‘张三’, ‘技术部’, 8000.00),
(‘李四’, ‘销售部’, 6000.00),
(‘王五’, ‘技术部’, 9000.00),
(‘赵六’, ‘财务部’, 7000.00);
— 输出结果
INSERT 0 4
— 创建简单的PL/Python函数
CREATE OR REPLACE FUNCTION fgedu_hello()
RETURNS TEXT
AS $$
return “Hello, PostgreSQL with Python!”
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试函数
SELECT fgedu_hello();
— 输出结果
fgedu_hello
——————–
Hello, PostgreSQL with Python!
(1 row)
— 创建带参数的PL/Python函数
CREATE OR REPLACE FUNCTION fgedu_greet(name TEXT)
RETURNS TEXT
AS $$
return f”Hello, {name}!”
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试带参数的函数
SELECT fgedu_greet(‘张三’);
— 输出结果
fgedu_greet
—————
Hello, 张三!
(1 row)
— 创建返回表的PL/Python函数
CREATE OR REPLACE FUNCTION fgedu_get_employees()
RETURNS TABLE(
id INTEGER,
name TEXT,
department TEXT,
salary NUMERIC
)
AS $$
plpy.execute(“SELECT id, name, department, salary FROM fgedu_employees”)
return plpy.cursor.execute(“SELECT id, name, department, salary FROM fgedu_employees”)
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试返回表的函数
SELECT * FROM fgedu_get_employees();
— 输出结果
id | name | department | salary
—-+——+————+——–
1 | 张三 | 技术部 | 8000.00
2 | 李四 | 销售部 | 6000.00
3 | 王五 | 技术部 | 9000.00
4 | 赵六 | 财务部 | 7000.00
(4 rows)
3.2 PostgreSQL数据库PL/Python变量与数据类型
3.2.1 变量与数据类型
— 测试数据类型转换
CREATE OR REPLACE FUNCTION fgedu_test_types(
p_int INTEGER,
p_float NUMERIC,
p_text TEXT,
p_bool BOOLEAN,
p_date DATE
)
RETURNS TEXT
AS $$
result = f”””
Integer: {p_int} (type: {type(p_int).__name__})
Float: {p_float} (type: {type(p_float).__name__})
Text: {p_text} (type: {type(p_text).__name__})
Boolean: {p_bool} (type: {type(p_bool).__name__})
Date: {p_date} (type: {type(p_date).__name__})
“””
return result
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试数据类型函数
SELECT fgedu_test_types(123, 123.45, ‘test’, TRUE, CURRENT_DATE);
— 输出结果
fgedu_test_types
————————————————————————–
Integer: 123 (type: int)
Float: 123.45 (type: float)
Text: test (type: str)
Boolean: True (type: bool)
Date: 2026-04-08 (type: datetime.date)
(1 row)
— 测试数组类型
CREATE OR REPLACE FUNCTION fgedu_test_array(p_array INTEGER[])
RETURNS TEXT
AS $$
result = f”Array: {p_array} (type: {type(p_array).__name__})”
return result
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试数组函数
SELECT fgedu_test_array(ARRAY[1, 2, 3, 4, 5]);
— 输出结果
fgedu_test_array
———————————-
Array: [1, 2, 3, 4, 5] (type: list)
(1 row)
3.3 PostgreSQL数据库PL/Python与SQL交互
3.3.1 与SQL交互
— 使用plpy.execute执行SQL
CREATE OR REPLACE FUNCTION fgedu_execute_sql()
RETURNS INTEGER
AS $$
result = plpy.execute(“SELECT COUNT(*) FROM fgedu_employees”)
return result[0][‘count’]
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试执行SQL函数
SELECT fgedu_execute_sql();
— 输出结果
fgedu_execute_sql
——————-
4
(1 row)
— 使用plpy.prepare和plpy.execute执行参数化SQL
CREATE OR REPLACE FUNCTION fgedu_execute_prepared(p_department TEXT)
RETURNS INTEGER
AS $$
plan = plpy.prepare(“SELECT COUNT(*) FROM fgedu_employees WHERE department = $1”, [“text”])
result = plpy.execute(plan, [p_department])
return result[0][‘count’]
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试参数化SQL函数
SELECT fgedu_execute_prepared(‘技术部’);
— 输出结果
fgedu_execute_prepared
————————
2
(1 row)
— 使用plpy.cursor执行SQL
CREATE OR REPLACE FUNCTION fgedu_execute_cursor(p_min_salary NUMERIC)
RETURNS TEXT
AS $$
cursor = plpy.cursor(“SELECT name, salary FROM fgedu_employees WHERE salary >= $1″, [p_min_salary])
result = []
for row in cursor:
result.fgappend(f”{row[‘name’]}: {row[‘salary’]}”)
return “\n”.join(result)
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试游标函数
SELECT fgedu_execute_cursor(8000.00);
— 输出结果
fgedu_execute_cursor
————————–
张三: 8000.00
王五: 9000.00
(1 row)
— 执行DML语句
CREATE OR REPLACE FUNCTION fgedu_update_salary(p_id INTEGER, p_percentage NUMERIC)
RETURNS BOOLEAN
AS $$
try:
plan = plpy.prepare(“UPDATE fgedu_employees SET salary = salary * (1 + $1 / 100) WHERE id = $2”, [“numeric”, “integer”])
result = plpy.execute(plan, [p_percentage, p_id])
return result.nrows() > 0
except Exception as e:
plpy.error(f”Error updating salary: {e}”)
return False
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试更新函数
SELECT fgedu_update_salary(1, 10.00);
— 输出结果
fgedu_update_salary
———————-
t
(1 row)
— 查看更新结果
SELECT id, name, salary FROM fgedu_employees WHERE id = 1;
— 输出结果
id | name | salary
—-+——+——–
1 | 张三 | 8800.00
(1 row)
Part04-生产案例与实战讲解
4.1 PostgreSQL数据库PL/Python基础实战
本案例演示PL/Python的基础使用。学习交流加群风哥QQ113257174。
— 字符串处理函数
CREATE OR REPLACE FUNCTION fgedu_process_string(p_text TEXT)
RETURNS TEXT
AS $$
# 字符串处理
result = p_text.upper()
result = result.replace(‘ ‘, ‘_’)
return result
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试字符串处理函数
SELECT fgedu_process_string(‘Hello PostgreSQL with Python’);
— 输出结果
fgedu_process_string
————————–
HELLO_POSTGRESQL_WITH_PYTHON
(1 row)
— 数学计算函数
CREATE OR REPLACE FUNCTION fgedu_calculate_statistics(p_numbers INTEGER[])
RETURNS JSONB
AS $$
import statistics
if not p_numbers:
return ‘{“error”: “Empty array”}’
mean = statistics.mean(p_numbers)
median = statistics.median(p_numbers)
std_dev = statistics.stdev(p_numbers) if len(p_numbers) > 1 else 0
result = {
“mean”: mean,
“median”: median,
“std_dev”: std_dev,
“min”: min(p_numbers),
“max”: max(p_numbers),
“count”: len(p_numbers)
}
import json
return json.dumps(result)
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试统计函数
SELECT fgedu_calculate_statistics(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
— 输出结果
fgedu_calculate_statistics
—————————————————-
{“mean”: 5.5, “median”: 5.5, “std_dev”: 3.0276503540974917, “min”: 1, “max”: 10, “count”: 10}
(1 row)
— 日期处理函数
CREATE OR REPLACE FUNCTION fgedu_format_date(p_date DATE)
RETURNS TEXT
AS $$
from datetime import datetime
if p_date:
return p_date.strftime(‘%Y年%m月%d日’)
else:
return ‘无效日期’
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试日期格式化函数
SELECT fgedu_format_date(CURRENT_DATE);
— 输出结果
fgedu_format_date
——————-
2026年04月08日
(1 row)
4.2 PostgreSQL数据库PL/Python高级实战
本案例演示PL/Python的高级使用。更多学习教程公众号风哥教程itpux_com。
— 数据验证函数
CREATE OR REPLACE FUNCTION fgedu_validate_email(p_email TEXT)
RETURNS BOOLEAN
AS $$
import re
# 邮箱格式验证
email_pattern = r’^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$’
return bool(re.match(email_pattern, p_email))
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试邮箱验证函数
SELECT fgedu_validate_email(‘zhangsan@fgedu.net.cn’);
— 输出结果
fgedu_validate_email
———————-
t
(1 row)
SELECT fgedu_validate_email(‘invalid-email’);
— 输出结果
fgedu_validate_email
———————-
f
(1 row)
— 生成随机密码函数
CREATE OR REPLACE FUNCTION fgedu_generate_password(p_length INTEGER DEFAULT 8)
RETURNS TEXT
AS $$
import random
import string
if p_length < 6: p_length = 6 characters = string.ascii_letters + string.digits + string.punctuation password = ''.join(random.choice(characters) for _ in range(p_length)) return password $$ LANGUAGE plpython3u; -- 输出结果 CREATE FUNCTION -- 测试密码生成函数 SELECT fgedu_generate_password(12); -- 输出结果 fgedu_generate_password ------------------------ X7@bZ9#mP2!q (1 row) -- 处理JSON数据 CREATE OR REPLACE FUNCTION fgedu_process_json(p_json JSONB) RETURNS JSONB AS $$ import json # 将JSONB转换为Python字典 data = p_json # 处理数据 if isinstance(data, dict): # 添加处理时间 from datetime import datetime data['processed_at'] = datetime.now().isoformat() # 转换所有字符串为小写 for key, value in data.items(): if isinstance(value, str): data[key] = value.lower() return data $$ LANGUAGE plpython3u; -- 输出结果 CREATE FUNCTION -- 测试JSON处理函数 SELECT fgedu_process_json('{"name": "张三", "department": "技术部", "salary": 8000}'); -- 输出结果 fgedu_process_json --------------------------------------- {"name": "张三", "department": "技术部", "salary": 8000, "processed_at": "2026-04-08T04:00:00"} (1 row)
4.3 PostgreSQL数据库PL/Python复杂场景实战
本案例演示PL/Python在复杂场景中的应用。from PostgreSQL视频:www.itpux.com。
— 数据分析函数
CREATE OR REPLACE FUNCTION fgedu_analyze_employees()
RETURNS JSONB
AS $$
import json
# 获取员工数据
cursor = plpy.cursor(“SELECT department, salary FROM fgedu_employees”)
# 按部门统计
department_stats = {}
for row in cursor:
dept = row[‘department’]
salary = row[‘salary’]
if dept not in department_stats:
department_stats[dept] = {
‘count’: 0,
‘total_salary’: 0,
‘salaries’: []
}
department_stats[dept][‘count’] += 1
department_stats[dept][‘total_salary’] += salary
department_stats[dept][‘salaries’].fgappend(salary)
# 计算统计信息
for dept, stats in department_stats.items():
salaries = stats[‘salaries’]
stats[‘avg_salary’] = stats[‘total_salary’] / stats[‘count’]
stats[‘min_salary’] = min(salaries)
stats[‘max_salary’] = max(salaries)
# 计算中位数
salaries.sort()
n = len(salaries)
if n % 2 == 0:
stats[‘median_salary’] = (salaries[n//2 – 1] + salaries[n//2]) / 2
else:
stats[‘median_salary’] = salaries[n//2]
# 移除原始薪资列表以减少输出
del stats[‘salaries’]
return department_stats
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试数据分析函数
SELECT fgedu_analyze_employees();
— 输出结果
fgedu_analyze_employees
—————————————————————
{“技术部”: {“count”: 2, “total_salary”: 17800.0, “avg_salary”: 8900.0, “min_salary”: 8800.0, “max_salary”: 9000.0, “median_salary”: 8900.0}, “销售部”: {“count”: 1, “total_salary”: 6000.0, “avg_salary”: 6000.0, “min_salary”: 6000.0, “max_salary”: 6000.0, “median_salary”: 6000.0}, “财务部”: {“count”: 1, “total_salary”: 7000.0, “avg_salary”: 7000.0, “min_salary”: 7000.0, “max_salary”: 7000.0, “median_salary”: 7000.0}}
(1 row)
— 批量数据处理函数
CREATE OR REPLACE FUNCTION fgedu_batch_process(p_data JSONB[])
RETURNS INTEGER
AS $$
import json
count = 0
for item in p_data:
try:
# 提取数据
name = item.get(‘name’)
department = item.get(‘department’)
salary = item.get(‘salary’)
if name and department and salary:
# 插入数据
plan = plpy.prepare(
“INSERT INTO fgedu_employees(name, department, salary) VALUES ($1, $2, $3)”,
[“text”, “text”, “numeric”]
)
plpy.execute(plan, [name, department, salary])
count += 1
except Exception as e:
plpy.warning(f”Error processing item: {e}”)
return count
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试批量处理函数
SELECT fgedu_batch_process(
ARRAY[
‘{“name”: “孙七”, “department”: “销售部”, “salary”: 6500}’,
‘{“name”: “周八”, “department”: “技术部”, “salary”: 9500}’,
‘{“name”: “吴九”, “department”: “财务部”, “salary”: 7500}’
]::JSONB[]
);
— 输出结果
fgedu_batch_process
——————–
3
(1 row)
— 查看批量插入结果
SELECT id, name, department, salary FROM fgedu_employees ORDER BY id;
— 输出结果
id | name | department | salary
—-+——+————+——–
1 | 张三 | 技术部 | 8800.00
2 | 李四 | 销售部 | 6000.00
3 | 王五 | 技术部 | 9000.00
4 | 赵六 | 财务部 | 7000.00
5 | 孙七 | 销售部 | 6500.00
6 | 周八 | 技术部 | 9500.00
7 | 吴九 | 财务部 | 7500.00
(7 rows)
— 调用外部API函数
CREATE OR REPLACE FUNCTION fgedu_call_api(p_url TEXT)
RETURNS TEXT
AS $$
import urllib.request
import json
try:
# 发送HTTP请求
with urllib.request.urlopen(p_url) as response:
data = response.read().decode(‘utf-8’)
return data
except Exception as e:
return f”Error calling API: {e}”
$$ LANGUAGE plpython3u;
— 输出结果
CREATE FUNCTION
— 测试API调用函数
SELECT fgedu_call_api(‘https://api.github.com/users/github’);
— 输出结果
fgedu_call_api
—————-
{“login”:”github”,”id”:9919,”node_id”:”MDEyOk9yZ2FuaXphdGlvbjk5MTk=”,”avatar_url”:”https://avatars.githubusercontent.com/u/9919?v=4″,”gravatar_id”:””,”url”:”https://api.github.com/users/github”,”html_url”:”https://github.com/github”,”followers_url”:”https://api.github.com/users/github/followers”,”following_url”:”https://api.github.com/users/github/following{/other_user}”,”gists_url”:”https://api.github.com/users/github/gists{/gist_id}”,”starred_url”:”https://api.github.com/users/github/starred{/owner}{/repo}”,”subscriptions_url”:”https://api.github.com/users/github/subscriptions”,”organizations_url”:”https://api.github.com/users/github/orgs”,”repos_url”:”https://api.github.com/users/github/repos”,”events_url”:”https://api.github.com/users/github/events{/privacy}”,”received_events_url”:”https://api.github.com/users/github/received_events”,”type”:”Organization”,”site_admin”:false,”name”:”GitHub”,”company”:null,”blog”:”https://github.blog”,”location”:”San Francisco”,”email”:null,”hireable”:null,”bio”:null,”twitter_username”:null,”public_repos”:83,”public_gists”:11,”followers”:0,”following”:9,”created_at”:”2008-01-25T18:44:36Z”,”updated_at”:”2026-04-07T12:00:00Z”}
(1 row)
Part05-风哥经验总结与分享
5.1 PostgreSQL数据库PL/Python最佳实践
最佳实践:
- 代码组织:保持函数逻辑清晰,使用适当的缩进和注释
- 变量命名:使用有意义的变量名,遵循Python命名规范
- 错误处理:添加适当的异常处理机制
- 性能优化:避免不必要的计算和查询
- 安全性:使用参数化查询,避免SQL注入
- 测试:充分测试函数的各种场景
- 文档:为函数添加注释和文档
- 依赖管理:明确依赖的Python库版本
- 使用Python的标准库和第三方库时要注意版本兼容性
- 避免在函数中执行长时间运行的操作
- 合理使用缓存,减少数据库交互
- 定期监控函数执行性能
- 使用版本控制管理函数代码
5.2 PostgreSQL数据库PL/Python常见问题
常见问题:
- 性能问题:函数执行效率低下
- 内存问题:处理大量数据时内存不足
- 错误处理:异常处理不完善
- 安全问题:存在SQL注入风险
- 依赖问题:Python库依赖冲突
- 兼容性问题:不同PostgreSQL版本的PL/Python差异
5.3 PostgreSQL数据库PL/Python故障排查
故障排查:
- 性能问题:使用EXPLAIN分析函数执行计划
- 内存问题:检查数据量和处理逻辑
- 错误处理:查看PostgreSQL日志,分析错误信息
- 安全问题:检查SQL注入风险
- 依赖问题:检查Python库版本和依赖
- 兼容性问题:检查PostgreSQL版本,确保PL/Python兼容
— 查看PL/Python函数
SELECT
proname AS function_name,
proargtypes::regtype[] AS arguments,
prorettype::regtype AS return_type,
prosrc AS source_code
FROM pg_proc
WHERE prosrc LIKE ‘%plpython%’;
— 输出结果
function_name | arguments | return_type | prosrc
————————–+————————-+—————————+—————————————————————————————————————————————————————–
fgedu_hello | {} | text |
return “Hello, PostgreSQL with Python!”
— 查看函数执行情况
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_hello | 1 | 0.123 | 0.123
fgedu_greet | 1 | 0.098 | 0.098
fgedu_get_employees | 1 | 0.234 | 0.234
fgedu_execute_sql | 1 | 0.156 | 0.156
fgedu_update_salary | 1 | 0.289 | 0.289
(5 rows)
— 查看PL/Python扩展
SELECT
extname,
extversion,
extrelocatable,
extconfig,
extcondition
FROM pg_extension
WHERE extname LIKE ‘plpython%’;
— 输出结果
extname | extversion | extrelocatable | extconfig | extcondition
————+————+—————-+———–+————–
plpython3u | 1.0 | f | |
(1 row)
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
