CodeGym /课程 /SQL SELF /PL/pgSQL调试入门

PL/pgSQL调试入门

SQL SELF
第 55 级 , 课程 0
可用

想象一下,你写了个很复杂的函数或者过程。你已经觉得你的数据库超牛了,结果——啪!——数据不对,查询慢得要死,老板开始着急了。这时候,调试就要上场了。

PL/pgSQL里的调试主要是为了:

  • 找出逻辑错误,比如函数返回的结果和你预期的不一样。
  • 搞清楚输入数据有啥问题。因为有时候数据库用户输入的东西,不只是数据,简直就是……完全看不懂的玩意!
  • 解决性能问题。毕竟,赶工写出来的代码,跑起来可能像在撒哈拉沙漠里找Wi-Fi的乌龟一样慢。

说真的,调试不仅仅是找bug和修bug。它其实是让你的代码更快、更高效、更容易读懂的好办法。

PL/pgSQL调试的主要方法

PL/pgSQL的调试有好几种方式。咱们一个个来看。

  1. 用PostgreSQL自带的工具

PostgreSQL自带了不少诊断功能,比如日志函数(RAISE NOTICERAISE EXCEPTION),还有查询执行计划分析(EXPLAIN ANALYZE)。这些工具能帮你搞清楚你的函数里到底发生了啥。

  1. RAISE NOTICE做日志

RAISE NOTICE就是你的好朋友,想知道函数里数据怎么流转的,哪里出问题了,或者想看看变量的值,都可以用它。和RAISE EXCEPTION不一样,它不会让函数直接崩掉。比如,你可以在每一步都输出变量内容。

DO $$
DECLARE
    counter INT := 0;
BEGIN
    FOR counter IN 1..5 LOOP
        RAISE NOTICE '当前计数器的值: %', counter;
    END LOOP;
END $$;

这段代码会输出counter从1到5的值。简单又神奇,调试的时候超有用!

  1. 用第三方工具

PL/pgSQL的调试还可以用一些工具,比如pgAdmin(带GUI界面)。它能让你打断点,实时看变量的值。如果你喜欢可视化的助手,pgAdmin绝对是你的好搭档。

调试的步骤

开始调试函数或过程时,最好按一定的顺序来。下面每一步都说说:

  1. 分析输入数据

第一步要搞清楚的,就是输入数据。确保你的函数拿到的数据没问题,没有奇怪的值。比如,你可以用RAISE NOTICE检查所有输入参数:

CREATE FUNCTION check_input(x INTEGER) RETURNS VOID AS $$
BEGIN
    IF x IS NULL THEN
        RAISE EXCEPTION '输入值不能为NULL!';
    END IF;
    RAISE NOTICE '输入值: %', x;
END;
$$ LANGUAGE plpgsql;

这个例子就是教你怎么提醒用户输入数据有问题。

  1. 检查每一步的执行

把你的函数拆成逻辑块,在关键点加上RAISE NOTICE。这样你就能知道到底哪一步出错了。

CREATE FUNCTION calculate_discount(price NUMERIC, discount NUMERIC) RETURNS NUMERIC AS $$
BEGIN
    RAISE NOTICE '函数开始: 价格 %, 折扣 %', price, discount;

    IF price <= 0 THEN
        RAISE EXCEPTION '价格不能为负或等于零!';
    END IF;

    IF discount < 0 OR discount > 100 THEN
        RAISE EXCEPTION '折扣必须在0到100之间!';
    END IF;

    RETURN price - (price * discount / 100);
END;
$$ LANGUAGE plpgsql;

这里每一步调试都会输出有用的信息,方便你了解进展。

  1. 优化和解决问题
  2. 找到bug后就修掉。如果是性能问题,用EXPLAIN ANALYZE这样的分析工具来优化你的查询。

调试技能的实际应用

来看个实际例子:有个函数,往表里加一条记录并返回生成的id。看起来很简单,但有时候函数会报错,我们就得搞清楚为啥。

原始函数:

CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
    new_id INTEGER;
BEGIN
    INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;
    RETURN new_id;
END;
$$ LANGUAGE plpgsql;

如果你传了不对的数据,比如age < 0,它就会报错。我们来用调试手段把它优化一下。

加了日志的优化版函数:

CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
    new_id INTEGER;
BEGIN
    -- 记录输入数据
    RAISE NOTICE '添加学生: 姓名 %, 年龄 %', name, age;

    -- 检查年龄是否合法
    IF age < 0 THEN
        RAISE EXCEPTION '年龄不能为负!';
    END IF;

    -- 添加学生并返回ID
    INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;

    -- 记录成功信息
    RAISE NOTICE '学生已添加,ID为 %', new_id;

    RETURN new_id;
END;
$$ LANGUAGE plpgsql;

现在,如果出错,你就能通过RAISE NOTICE的消息,准确知道哪里出问题了。

最后的一些小建议

  1. 记得把没用的日志删掉。 RAISE NOTICE调试时很好用,但上线后一直留着会让日志很乱。
  2. 多用小代码块。 如果函数太复杂,拆成几个小的。这样调试更轻松。
  3. 多练习。 你写和调试的代码越多,找bug和修bug就越快。

调试其实就像侦探游戏,只不过你用的是SQL语句和PL/pgSQL逻辑,不是放大镜。调试的本事是靠经验练出来的,每修好一个bug,你就更牛逼一点!

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION