1. 首页 > PostgreSQL教程 > 正文

PostgreSQL教程FG172-PG过程化语言避坑:常见执行错误解决

本文档风哥主要介绍PostgreSQL数据库过程化语言常见执行错误及解决方案,包括语法错误、运行时错误、权限错误、事务错误等内容,风哥教程参考PostgreSQL官方文档Server Programming、Error Codes等内容,适合DBA和开发人员在生产环境中使用。

Part01-基础概念与理论知识

1.1 PostgreSQL数据库过程化语言错误类型

PostgreSQL过程化语言执行过程中可能遇到多种类型的错误:语法错误是在编译阶段发现的错误,如SQL语法错误、变量未定义等;运行时错误是在执行阶段发生的错误,如除零错误、空指针引用等;权限错误是由于权限不足导致的错误;事务错误包括死锁、序列化失败等。更多视频教程www.fgedu.net.cn。

PostgreSQL数据库过程化语言常见错误类型:

  • 语法错误:SQL语法错误、PL/pgSQL语法错误、类型不匹配
  • 运行时错误:除零错误、数组越界、空值处理
  • 权限错误:表访问权限、函数执行权限、序列使用权限
  • 事务错误:死锁、序列化失败、约束违反
  • 资源错误:内存不足、磁盘空间不足、连接超时

1.2 PostgreSQL数据库错误代码解析

PostgreSQL使用标准SQLSTATE错误代码来标识不同类型的错误。错误代码由5个字符组成,前两个字符表示错误类别,后三个字符表示具体错误。学习交流加群风哥微信: itpux-com。常见的错误代码包括:42xxx表示语法错误,23xxx表示完整性约束违反,53xxx表示资源不足,57xxx表示系统错误等。

— 查看PostgreSQL错误代码
SELECT errcode, name, description
FROM pg_error_codes
WHERE errcode LIKE ‘42%’ OR errcode LIKE ‘23%’
ORDER BY errcode;

— 输出结果
errcode | name | description
———+—————————+—————————————-
23001 | restrict_violation | restrict violation
23502 | not_null_violation | not-null violation
23503 | foreign_key_violation | foreign key violation
23505 | unique_violation | unique violation
23514 | check_violation | check violation
42000 | syntax_error_or_access_rule_violation | syntax error or access rule violation
42601 | syntax_error | syntax error
42602 | invalid_name | invalid name
42622 | name_too_long | name too long
42702 | ambiguous_column | ambiguous column
42703 | undefined_column | undefined column
42804 | datatype_mismatch | datatype mismatch
42883 | undefined_function | undefined function
42P01 | undefined_table | undefined table
42P02 | undefined_parameter | undefined parameter
(15 rows)

— 在PL/pgSQL中获取错误代码
DO $$
BEGIN
BEGIN
SELECT 1/0;
EXCEPTION
WHEN division_by_zero THEN
RAISE NOTICE ‘错误代码: %, 错误信息: %’, SQLSTATE, SQLERRM;
END;
END;
$$;

— 输出结果
NOTICE: 错误代码: 22012, 错误信息: division by zero
DO

1.3 PostgreSQL数据库错误处理机制

PostgreSQL过程化语言提供了完善的错误处理机制。在PL/pgSQL中使用EXCEPTION块捕获和处理错误;可以使用RAISE语句抛出自定义错误;可以使用GET STACKED DIAGNOSTICS获取详细的错误信息。合理的错误处理可以保证程序的健壮性,避免因错误导致数据不一致。

— PL/pgSQL错误处理示例
CREATE OR REPLACE FUNCTION fgedu_safe_divide(
p_dividend NUMERIC,
p_divisor NUMERIC
)
RETURNS NUMERIC
AS $$
DECLARE
v_result NUMERIC;
BEGIN
IF p_divisor = 0 THEN
RAISE EXCEPTION ‘除数不能为零’;
END IF;

v_result := p_dividend / p_divisor;
RETURN v_result;

EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE ‘计算错误: %’, SQLERRM;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

— 测试错误处理
SELECT fgedu_safe_divide(10, 0);

— 输出结果
NOTICE: 计算错误: 除数不能为零
fgedu_safe_divide
——————–
NULL
(1 row)

Part02-生产环境规划与建议

2.1 PostgreSQL数据库错误预防策略

预防错误比处理错误更重要。预防策略包括:编写代码前仔细设计,确保逻辑正确;使用参数验证,检查输入参数的有效性;使用断言检查关键条件;编写单元测试验证代码正确性;代码审查发现潜在问题。

— 参数验证示例
CREATE OR REPLACE PROCEDURE fgedu_transfer_money(
p_from_account INTEGER,
p_to_account INTEGER,
p_amount NUMERIC(15,2)
)
AS $$
DECLARE
v_balance NUMERIC(15,2);
BEGIN
— 参数验证
IF p_amount IS NULL OR p_amount <= 0 THEN RAISE EXCEPTION '转账金额必须大于0'; END IF; IF p_from_account IS NULL OR p_to_account IS NULL THEN RAISE EXCEPTION '账户ID不能为空'; END IF; IF p_from_account = p_to_account THEN RAISE EXCEPTION '转出账户和转入账户不能相同'; END IF; -- 检查账户是否存在 IF NOT EXISTS (SELECT 1 FROM fgedu_accounts WHERE id = p_from_account) THEN RAISE EXCEPTION '转出账户不存在: %', p_from_account; END IF; IF NOT EXISTS (SELECT 1 FROM fgedu_accounts WHERE id = p_to_account) THEN RAISE EXCEPTION '转入账户不存在: %', p_to_account; END IF; -- 检查余额 SELECT balance INTO v_balance FROM fgedu_accounts WHERE id = p_from_account FOR UPDATE; IF v_balance < p_amount THEN RAISE EXCEPTION '余额不足,当前余额: %,需要: %', v_balance, p_amount; END IF; -- 执行转账 UPDATE fgedu_accounts SET balance = balance - p_amount WHERE id = p_from_account; UPDATE fgedu_accounts SET balance = balance + p_amount WHERE id = p_to_account; COMMIT; RAISE NOTICE '转账成功'; END; $$ LANGUAGE plpgsql;

2.2 PostgreSQL数据库错误日志配置

合理的日志配置有助于错误排查。配置要点:启用错误日志记录,设置log_error_verbosity = verbose;记录错误上下文,设置log_error_context = on;设置日志文件路径和轮转策略;对于关键错误,配置告警通知。

2.3 PostgreSQL数据库错误监控方案

建立错误监控机制:定期检查日志文件,统计错误发生频率;对高频错误进行分析,找出根本原因;建立错误告警机制,及时发现和处理问题。

风哥提示:错误处理是保证系统稳定性的关键。建议在开发阶段就建立完善的错误处理机制,包括参数验证、异常捕获、日志记录等。生产环境中要定期检查错误日志,及时发现和解决问题。

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

3.1 PostgreSQL数据库语法错误解决

3.1.1 SQL语法错误

— 错误示例:SQL语法错误
SELECT * FORM fgedu_users;

— 输出结果
ERROR: syntax error at or near “FORM”
LINE 1: SELECT * FORM fgedu_users;
^
SQL state: 42601

— 正确写法
SELECT * FROM fgedu_users;

— 输出结果
id | name | email
—-+——–+——————
1 | fgedu | fgedu@test.com
(1 row)

— 错误示例:字符串引号错误
SELECT * FROM fgedu_users WHERE name = “fgedu”;

— 输出结果
ERROR: column “fgedu” does not exist
LINE 1: SELECT * FROM fgedu_users WHERE name = “fgedu”;
^
SQL state: 42703

— 正确写法:PostgreSQL中字符串使用单引号
SELECT * FROM fgedu_users WHERE name = ‘fgedu’;

— 输出结果
id | name | email
—-+——–+——————
1 | fgedu | fgedu@test.com
(1 row)

— 错误示例:关键字冲突
CREATE TABLE order (id INTEGER, name VARCHAR(100));

— 输出结果
ERROR: syntax error at or near “order”
LINE 1: CREATE TABLE order (id INTEGER, name VARCHAR(100));
^
SQL state: 42601

— 正确写法:使用引号或避免关键字
CREATE TABLE “order” (id INTEGER, name VARCHAR(100));
— 或者使用更好的命名
CREATE TABLE fgedu_orders (id INTEGER, name VARCHAR(100));

— 输出结果
CREATE TABLE

3.1.2 PL/pgSQL语法错误

— 错误示例:变量未声明
CREATE OR REPLACE FUNCTION fgedu_test()
RETURNS INTEGER AS $$
BEGIN
v_count := 10; — 变量未声明
RETURN v_count;
END;
$$ LANGUAGE plpgsql;

— 输出结果
ERROR: “v_count” is not a known variable
LINE 3: v_count := 10;
^
SQL state: 42601

— 正确写法:先声明变量
CREATE OR REPLACE FUNCTION fgedu_test()
RETURNS INTEGER AS $$
DECLARE
v_count INTEGER;
BEGIN
v_count := 10;
RETURN v_count;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 错误示例:IF语句缺少THEN
CREATE OR REPLACE FUNCTION fgedu_check_value(p_value INTEGER)
RETURNS TEXT AS $$
BEGIN
IF p_value > 0
RETURN ‘positive’;
END IF;
END;
$$ LANGUAGE plpgsql;

— 输出结果
ERROR: syntax error at or near “RETURN”
LINE 4: RETURN ‘positive’;
^
SQL state: 42601

— 正确写法:IF语句需要THEN
CREATE OR REPLACE FUNCTION fgedu_check_value(p_value INTEGER)
RETURNS TEXT AS $$
BEGIN
IF p_value > 0 THEN
RETURN ‘positive’;
END IF;
RETURN ‘non-positive’;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 错误示例:分号缺失
CREATE OR REPLACE FUNCTION fgedu_get_count()
RETURNS INTEGER AS $$
DECLARE
v_count INTEGER
BEGIN
SELECT COUNT(*) INTO v_count FROM fgedu_users
RETURN v_count
END
$$ LANGUAGE plpgsql;

— 输出结果
ERROR: syntax error at or near “BEGIN”
LINE 3: BEGIN
^
SQL state: 42601

— 正确写法:语句结束需要分号
CREATE OR REPLACE FUNCTION fgedu_get_count()
RETURNS INTEGER AS $$
DECLARE
v_count INTEGER;
BEGIN
SELECT COUNT(*) INTO v_count FROM fgedu_users;
RETURN v_count;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

3.2 PostgreSQL数据库运行时错误解决

3.2.1 空值处理错误

— 错误示例:空值参与运算
SELECT NULL + 10;

— 输出结果
?column?
———-

(1 row)

— 错误示例:空值比较
SELECT * FROM fgedu_users WHERE name != NULL;

— 输出结果
id | name | email
—-+——+——-
(0 rows)

— 正确写法:使用IS NULL或IS NOT NULL
SELECT * FROM fgedu_users WHERE name IS NOT NULL;

— 输出结果
id | name | email
—-+——–+——————
1 | fgedu | fgedu@test.com
(1 row)

— 使用COALESCE处理空值
SELECT COALESCE(NULL, 0) + 10;

— 输出结果
?column?
———-
10
(1 row)

— PL/pgSQL中处理空值
CREATE OR REPLACE FUNCTION fgedu_safe_concat(
p_str1 TEXT,
p_str2 TEXT
)
RETURNS TEXT
AS $$
BEGIN
RETURN COALESCE(p_str1, ”) || COALESCE(p_str2, ”);
END;
$$ LANGUAGE plpgsql;

— 测试
SELECT fgedu_safe_concat(NULL, ‘world’);

— 输出结果
fgedu_safe_concat
——————-
world
(1 row)

3.2.2 类型转换错误

— 错误示例:类型转换失败
SELECT ‘abc’::INTEGER;

— 输出结果
ERROR: invalid input syntax for type integer: “abc”
SQL state: 22P02

— 正确写法:使用TRY-CATCH或先验证
CREATE OR REPLACE FUNCTION fgedu_safe_to_integer(p_text TEXT)
RETURNS INTEGER
AS $$
BEGIN
RETURN p_text::INTEGER;
EXCEPTION
WHEN INVALID_TEXT_REPRESENTATION THEN
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

— 测试
SELECT fgedu_safe_to_integer(‘123’);
SELECT fgedu_safe_to_integer(‘abc’);

— 输出结果
fgedu_safe_to_integer
———————–
123
(1 row)

fgedu_safe_to_integer
———————–
NULL
(1 row)

— 错误示例:日期格式转换
SELECT ‘2026-13-01’::DATE;

— 输出结果
ERROR: date/time field value out of range: “2026-13-01”
SQL state: 22008

— 正确写法:使用TO_DATE指定格式
SELECT TO_DATE(‘2026-01-01’, ‘YYYY-MM-DD’);

— 输出结果
to_date
————
2026-01-01
(1 row)

3.3 PostgreSQL数据库权限错误解决

— 错误示例:权限不足
— 以普通用户fgedu01执行
SELECT * FROM fgedu_sensitive_data;

— 输出结果
ERROR: permission denied for table fgedu_sensitive_data
SQL state: 42501

— 解决方案:授予权限
GRANT SELECT ON fgedu_sensitive_data TO fgedu01;

— 输出结果
GRANT

— 错误示例:函数执行权限
— 以普通用户执行超级用户函数
SELECT fgedu_admin_function();

— 输出结果
ERROR: permission denied for function fgedu_admin_function
SQL state: 42501

— 解决方案:授予函数执行权限
GRANT EXECUTE ON FUNCTION fgedu_admin_function() TO fgedu01;

— 输出结果
GRANT

— 错误示例:序列权限
INSERT INTO fgedu_orders(name) VALUES(‘test’);

— 输出结果
ERROR: permission denied for sequence fgedu_orders_id_seq
SQL state: 42501

— 解决方案:授予序列使用权限
GRANT USAGE, SELECT ON SEQUENCE fgedu_orders_id_seq TO fgedu01;

— 输出结果
GRANT

— 查看用户权限
SELECT
grantee,
table_schema,
table_name,
privilege_type
FROM information_schema.table_privileges
WHERE grantee = ‘fgedu01’;

— 输出结果
grantee | table_schema | table_name | privilege_type
———+————–+———————+—————-
fgedu01 | public | fgedu_sensitive_data| SELECT
fgedu01 | public | fgedu_orders | INSERT
(2 rows)

Part04-生产案例与实战讲解

4.1 PostgreSQL数据库PL/pgSQL常见错误实战

本案例演示PL/pgSQL中常见的错误及解决方案。学习交流加群风哥QQ113257174。

— 案例1:查询返回多行错误
CREATE OR REPLACE FUNCTION fgedu_get_user_name(p_email_pattern TEXT)
RETURNS TEXT
AS $$
DECLARE
v_name TEXT;
BEGIN
— 错误:如果匹配多行会报错
SELECT name INTO v_name
FROM fgedu_users
WHERE email LIKE p_email_pattern;

RETURN v_name;
END;
$$ LANGUAGE plpgsql;

— 测试(假设有多条匹配)
SELECT fgedu_get_user_name(‘%@test.com’);

— 输出结果
ERROR: query returned more than one row
SQL state: 21000

— 正确写法:使用LIMIT或循环
CREATE OR REPLACE FUNCTION fgedu_get_user_name(p_email_pattern TEXT)
RETURNS TEXT
AS $$
DECLARE
v_name TEXT;
BEGIN
SELECT name INTO v_name
FROM fgedu_users
WHERE email LIKE p_email_pattern
LIMIT 1;

RETURN v_name;
END;
$$ LANGUAGE plpgsql;

— 案例2:动态SQL错误
CREATE OR REPLACE FUNCTION fgedu_dynamic_query(p_table_name TEXT)
RETURNS INTEGER
AS $$
DECLARE
v_count INTEGER;
BEGIN
— 错误:不能直接使用变量作为表名
SELECT COUNT(*) INTO v_count FROM p_table_name;
RETURN v_count;
END;
$$ LANGUAGE plpgsql;

— 输出结果
ERROR: relation “p_table_name” does not exist
SQL state: 42P01

— 正确写法:使用EXECUTE执行动态SQL
CREATE OR REPLACE FUNCTION fgedu_dynamic_query(p_table_name TEXT)
RETURNS INTEGER
AS $$
DECLARE
v_count INTEGER;
BEGIN
EXECUTE ‘SELECT COUNT(*) FROM ‘ || p_table_name
INTO v_count;
RETURN v_count;
END;
$$ LANGUAGE plpgsql;

— 测试
SELECT fgedu_dynamic_query(‘fgedu_users’);

— 输出结果
fgedu_dynamic_query
———————
10
(1 row)

— 案例3:异常处理获取详细信息
CREATE OR REPLACE FUNCTION fgedu_insert_with_error_handling(
p_name TEXT,
p_email TEXT
)
RETURNS JSONB
AS $$
DECLARE
v_sqlstate TEXT;
v_sqlerrm TEXT;
v_detail TEXT;
v_hint TEXT;
BEGIN
BEGIN
INSERT INTO fgedu_users(name, email) VALUES(p_name, p_email);
RETURN jsonb_build_object(‘success’, true, ‘message’, ‘插入成功’);

EXCEPTION
WHEN unique_violation THEN
GET STACKED DIAGNOSTICS
v_sqlstate = RETURNED_SQLSTATE,
v_sqlerrm = MESSAGE_TEXT,
v_detail = PG_EXCEPTION_DETAIL,
v_hint = PG_EXCEPTION_HINT;

RETURN jsonb_build_object(
‘success’, false,
‘error_code’, v_sqlstate,
‘error_message’, v_sqlerrm,
‘detail’, v_detail,
‘hint’, v_hint
);

WHEN OTHERS THEN
GET STACKED DIAGNOSTICS
v_sqlstate = RETURNED_SQLSTATE,
v_sqlerrm = MESSAGE_TEXT;

RETURN jsonb_build_object(
‘success’, false,
‘error_code’, v_sqlstate,
‘error_message’, v_sqlerrm
);
END;
END;
$$ LANGUAGE plpgsql;

— 测试唯一约束违反
SELECT fgedu_insert_with_error_handling(‘fgedu’, ‘fgedu@test.com’);

— 输出结果
fgedu_insert_with_error_handling
———————————————————————————————————
{“success”: false, “error_code”: “23505”, “error_message”: “duplicate key value violates unique constraint…”, “detail”: “Key (email)=(fgedu@test.com) already exists.”, “hint”: null}
(1 row)

4.2 PostgreSQL数据库PL/Python常见错误实战

本案例演示PL/Python中常见的错误及解决方案。更多学习教程公众号风哥教程itpux_com。

— 案例1:Python异常处理
CREATE OR REPLACE FUNCTION fgedu_python_divide(
p_a NUMERIC,
p_b NUMERIC
)
RETURNS NUMERIC
AS $$
try:
return p_a / p_b
except ZeroDivisionError:
plpy.warning(“除数为零,返回NULL”)
return None
except Exception as e:
plpy.error(f”计算错误: {str(e)}”)
return None
$$ LANGUAGE plpython3u;

— 测试
SELECT fgedu_python_divide(10, 0);

— 输出结果
WARNING: 除数为零,返回NULL
fgedu_python_divide
——————–
NULL
(1 row)

— 案例2:数据库查询错误处理
CREATE OR REPLACE FUNCTION fgedu_python_query(p_table_name TEXT)
RETURNS INTEGER
AS $$
try:
query = f”SELECT COUNT(*) FROM {p_table_name}”
result = plpy.execute(query)
return result[0][‘count’]
except Exception as e:
plpy.warning(f”查询失败: {str(e)}”)
return -1
$$ LANGUAGE plpython3u;

— 测试不存在的表
SELECT fgedu_python_query(‘nonexistent_table’);

— 输出结果
WARNING: 查询失败: relation “nonexistent_table” does not exist
fgedu_python_query
——————–
-1
(1 row)

— 案例3:类型转换错误
CREATE OR REPLACE FUNCTION fgedu_python_safe_int(p_value TEXT)
RETURNS INTEGER
AS $$
try:
return int(p_value)
except ValueError:
plpy.notice(f”无法将 ‘{p_value}’ 转换为整数”)
return None
$$ LANGUAGE plpython3u;

— 测试
SELECT fgedu_python_safe_int(‘123’);
SELECT fgedu_python_safe_int(‘abc’);

— 输出结果
NOTICE: 无法将 ‘abc’ 转换为整数
fgedu_python_safe_int
———————–
NULL
(1 row)

4.3 PostgreSQL数据库事务相关错误实战

本案例演示事务相关的错误及解决方案。from PostgreSQL视频:www.itpux.com。

— 案例1:死锁处理
— 会话1
BEGIN;
UPDATE fgedu_accounts SET balance = balance – 100 WHERE id = 1;
— 不提交,等待

— 会话2
BEGIN;
UPDATE fgedu_accounts SET balance = balance – 100 WHERE id = 2;
— 尝试更新会话1锁定的行
UPDATE fgedu_accounts SET balance = balance + 100 WHERE id = 1;

— 输出结果
ERROR: deadlock detected
DETAIL: Process 12345 waits for ShareLock on transaction 67890; blocked by process 12346.
SQL state: 40P01

— 解决方案:设置锁等待超时
SET lock_timeout = ‘5s’;
BEGIN;
UPDATE fgedu_accounts SET balance = balance – 100 WHERE id = 1;
— 如果等待超过5秒,自动取消

— 案例2:序列化失败
— 设置隔离级别为SERIALIZABLE
SET default_transaction_isolation = ‘serializable’;

— 会话1
BEGIN;
SELECT * FROM fgedu_accounts WHERE id = 1;
— 不提交

— 会话2
BEGIN;
UPDATE fgedu_accounts SET balance = balance + 100 WHERE id = 1;
COMMIT;

— 会话1继续
UPDATE fgedu_accounts SET balance = balance – 100 WHERE id = 1;
COMMIT;

— 输出结果
ERROR: could not serialize access due to concurrent update
SQL state: 40001

— 解决方案:使用重试机制
CREATE OR REPLACE PROCEDURE fgedu_retry_transaction(
p_max_retries INTEGER DEFAULT 3
)
AS $$
DECLARE
v_attempt INTEGER := 0;
BEGIN
WHILE v_attempt < p_max_retries LOOP v_attempt := v_attempt + 1; BEGIN -- 设置隔离级别 SET LOCAL default_transaction_isolation = 'serializable'; -- 执行业务逻辑 UPDATE fgedu_accounts SET balance = balance - 100 WHERE id = 1; UPDATE fgedu_accounts SET balance = balance + 100 WHERE id = 2; COMMIT; RAISE NOTICE '事务成功,尝试次数: %', v_attempt; RETURN; EXCEPTION WHEN serialization_failure THEN ROLLBACK; IF v_attempt >= p_max_retries THEN
RAISE EXCEPTION ‘事务重试%次后仍失败’, p_max_retries;
END IF;
RAISE NOTICE ‘序列化失败,第%次重试…’, v_attempt;
END;
END LOOP;
END;
$$ LANGUAGE plpgsql;

— 案例3:约束违反错误
CREATE TABLE fgedu_products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price NUMERIC(10,2) CHECK (price > 0),
stock INTEGER DEFAULT 0 CHECK (stock >= 0)
);

— 插入违反约束的数据
INSERT INTO fgedu_products(name, price, stock) VALUES(‘test’, -10, 5);

— 输出结果
ERROR: new row for relation “fgedu_products” violates check constraint “fgedu_products_price_check”
DETAIL: Failing row contains (1, test, -10.00, 5).
SQL state: 23514

— 解决方案:使用存储过程验证
CREATE OR REPLACE PROCEDURE fgedu_insert_product(
p_name VARCHAR(100),
p_price NUMERIC(10,2),
p_stock INTEGER DEFAULT 0
)
AS $$
BEGIN
— 参数验证
IF p_price IS NULL OR p_price <= 0 THEN RAISE EXCEPTION '价格必须大于0'; END IF; IF p_stock IS NULL OR p_stock < 0 THEN RAISE EXCEPTION '库存不能为负数'; END IF; INSERT INTO fgedu_products(name, price, stock) VALUES(p_name, p_price, p_stock); RAISE NOTICE '产品插入成功'; END; $$ LANGUAGE plpgsql; -- 测试 CALL fgedu_insert_product('test', -10, 5); -- 输出结果 ERROR: 价格必须大于0 SQL state: P0001

Part05-风哥经验总结与分享

5.1 PostgreSQL数据库错误处理最佳实践

错误处理最佳实践:预防优先,通过参数验证和断言预防错误;分类处理,针对不同类型的错误采用不同的处理策略;日志记录,详细记录错误信息便于排查;用户友好,返回有意义的错误信息;事务安全,确保错误不会导致数据不一致。

— 完整的错误处理模板
CREATE OR REPLACE FUNCTION fgedu_template_function(
p_param1 INTEGER,
p_param2 TEXT DEFAULT NULL
)
RETURNS JSONB
AS $$
DECLARE
v_result JSONB;
v_start_time TIMESTAMP;
v_error_detail TEXT;
BEGIN
v_start_time := clock_timestamp();

— 参数验证
IF p_param1 IS NULL THEN
RAISE EXCEPTION ‘参数p_param1不能为空’;
END IF;

— 业务逻辑
BEGIN
— 执行主要逻辑
v_result := jsonb_build_object(
‘param1’, p_param1,
‘param2’, p_param2,
‘status’, ‘success’
);

— 记录成功日志
INSERT INTO fgedu_function_logs(
function_name, params, status, exec_time, create_time
) VALUES (
‘fgedu_template_function’,
jsonb_build_object(‘p_param1’, p_param1, ‘p_param2’, p_param2),
‘success’,
clock_timestamp() – v_start_time,
NOW()
);

RETURN v_result;

EXCEPTION
WHEN unique_violation THEN
v_error_detail := SQLERRM;
RAISE NOTICE ‘唯一约束违反: %’, v_error_detail;
RETURN jsonb_build_object(
‘status’, ‘error’,
‘error_code’, ‘23505’,
‘error_message’, ‘数据已存在’
);

WHEN foreign_key_violation THEN
v_error_detail := SQLERRM;
RAISE NOTICE ‘外键约束违反: %’, v_error_detail;
RETURN jsonb_build_object(
‘status’, ‘error’,
‘error_code’, ‘23503’,
‘error_message’, ‘关联数据不存在’
);

WHEN OTHERS THEN
v_error_detail := SQLERRM;
RAISE WARNING ‘未知错误: %’, v_error_detail;

— 记录错误日志
INSERT INTO fgedu_function_logs(
function_name, params, status, error_message, create_time
) VALUES (
‘fgedu_template_function’,
jsonb_build_object(‘p_param1’, p_param1, ‘p_param2’, p_param2),
‘error’,
v_error_detail,
NOW()
);

RETURN jsonb_build_object(
‘status’, ‘error’,
‘error_code’, SQLSTATE,
‘error_message’, v_error_detail
);
END;
END;
$$ LANGUAGE plpgsql;

5.2 PostgreSQL数据库调试技巧

调试技巧:使用RAISE NOTICE输出调试信息;使用EXPLAIN ANALYZE分析SQL执行计划;使用pg_stat_statements查看SQL执行统计;将复杂逻辑拆分为小函数逐步调试。

— 调试示例
CREATE OR REPLACE FUNCTION fgedu_debug_function(p_id INTEGER)
RETURNS JSONB
AS $$
DECLARE
v_data RECORD;
v_count INTEGER;
v_result JSONB;
BEGIN
— 输出调试信息
RAISE NOTICE ‘开始处理,参数: %’, p_id;

— 检查数据
SELECT COUNT(*) INTO v_count FROM fgedu_orders WHERE customer_id = p_id;
RAISE NOTICE ‘找到 % 条订单记录’, v_count;

— 处理数据
FOR v_data IN SELECT * FROM fgedu_orders WHERE customer_id = p_id
LOOP
RAISE NOTICE ‘处理订单: %, 金额: %’, v_data.id, v_data.total_amount;
END LOOP;

— 返回结果
v_result := jsonb_build_object(
‘customer_id’, p_id,
‘order_count’, v_count,
‘status’, ‘completed’
);

RAISE NOTICE ‘处理完成’;
RETURN v_result;
END;
$$ LANGUAGE plpgsql;

— 执行调试
SELECT fgedu_debug_function(1001);

— 输出结果
NOTICE: 开始处理,参数: 1001
NOTICE: 找到 5 条订单记录
NOTICE: 处理订单: 1, 金额: 1250.00
NOTICE: 处理订单: 2, 金额: 3580.00
NOTICE: 处理完成
fgedu_debug_function
———————————————————
{“customer_id”: 1001, “order_count”: 5, “status”: “completed”}
(1 row)

5.3 PostgreSQL数据库常见问题汇总

常见问题汇总:语法错误通常是拼写错误或关键字冲突;运行时错误需要检查数据和逻辑;权限错误需要检查用户权限配置;事务错误需要优化事务逻辑和锁策略。

常见错误速查表:

  • 42601 – 语法错误:检查SQL语法,注意关键字冲突
  • 42P01 – 表不存在:检查表名拼写,检查schema
  • 42703 – 列不存在:检查列名拼写,检查表别名
  • 23505 – 唯一约束违反:检查重复数据
  • 23503 – 外键约束违反:检查关联数据是否存在
  • 42501 – 权限不足:检查用户权限配置
  • 40P01 – 死锁:优化事务逻辑,减少锁持有时间
  • 40001 – 序列化失败:使用重试机制
风哥提示:错误处理是开发中不可忽视的环节。建议在开发阶段就建立完善的错误处理机制,包括参数验证、异常捕获、日志记录等。生产环境中要定期检查错误日志,及时发现和解决问题。同时要建立错误处理规范,确保团队成员遵循统一的错误处理策略。

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

联系我们

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

微信号:itpux-com

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