1. 首页 > PostgreSQL教程 > 正文

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。

PostgreSQL数据库PL/Python特点:

  • 使用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。

— 检查PL/Python是否已安装
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依赖库。

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

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

3.1 PostgreSQL数据库PL/Python函数创建

3.1.1 函数创建

— PL/Python函数创建

— 创建测试表
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 变量与数据类型

— PL/Python变量与数据类型

— 测试数据类型转换
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交互

— PL/Python与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。

— PL/Python基础实战

— 字符串处理函数
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。

— PL/Python高级实战

— 数据验证函数
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。

— PL/Python复杂场景实战

— 数据分析函数
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库版本
PL/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)

风哥提示:PL/Python是PostgreSQL的强大过程化编程语言,允许使用Python语言编写数据库函数。在使用PL/Python时,需要注意:1) 保持函数逻辑清晰简洁;2) 充分测试性能和安全性;3) 建立完善的错误处理机制;4) 定期监控函数执行情况;5) 遵循Python和PostgreSQL的最佳实践。通过合理使用PL/Python,可以利用Python的丰富生态系统来扩展PostgreSQL的功能,实现更加复杂和灵活的业务逻辑。

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

联系我们

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

微信号:itpux-com

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