CodeGym /课程 /SQL SELF /在真实场景中使用事务的例子

在真实场景中使用事务的例子

SQL SELF
第 39 级 , 课程 3
可用

有时候事务就像超级英雄电影里的角色。他们在崩溃、出错或者故障时拯救我们的数据库。如果你在做一件需要多个操作、而且这些操作不能分开的事,事务能保证它们一起完成。咱们来看看支付处理的例子,搞明白它是怎么工作的。

支付处理

想象一个经典场景:你有两个银行账户,我们想把钱从一个账户转到另一个。这可不是“点一下按钮”就完事的操作。得确保我们正确地从一个账户扣钱、再加到另一个账户。任何错误都可能是灾难:要么两个账户都没变,要么余额乱套(比如钱凭空消失或者凭空多出来)。

场景:账户间转账

来看下我们的代码。仔细读,就像是来自遥远星系的信息:

-- 开始事务
BEGIN;

-- 步骤 1. 从发起人账户扣钱
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;

-- 步骤 2. 给收款人账户加钱
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;

-- 一切顺利?那就保存更改!
COMMIT;

这里什么最重要?

  • 如果在步骤 1步骤 2出错(比如SQL写错、余额不够),事务可以用ROLLBACK回滚,数据会回到原样。
  • COMMIT保证只有所有步骤都成功才会应用更改。

加个余额检查

那如果发起人余额不够转账咋办?咱们加个余额检查,别让他“变成负数”。

-- 开始事务
BEGIN;

-- 获取发起人当前余额
DO $$
DECLARE
    current_balance NUMERIC;
BEGIN
    SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;

    -- 检查钱够不够
    IF current_balance >= 100 THEN
        -- 钱够,执行转账
        UPDATE accounts
        SET balance = balance - 100
        WHERE account_id = 1;

        UPDATE accounts
        SET balance = balance + 100
        WHERE account_id = 2;

        -- 提交更改
        COMMIT;
    ELSE
        -- 钱不够,回滚
        ROLLBACK;
        RAISE NOTICE '余额不足,无法转账!';
    END IF;
END $$;

这里更有意思了吧?

  • 我们用PL/pgSQL块加了IF条件判断。如果余额小于需要的金额,事务就会被拒绝,啥都不会变。
  • ROLLBACK会撤销更改(虽然这段代码其实还没改啥,但这样写是个好习惯)。
注意!

这节课讲的是事务在真实场景下的用法,所以我特地举了个现实生活的例子。它包含了存储过程,用PL-SQL写的。我觉得你们已经有足够经验能看懂这里是怎么回事。以后我们还会回到PL-SQL的话题,讲更复杂的例子。

在事务里批量更新数据

事务不仅仅用在转账这种事上。比如我们有个网店数据库,每天有几十个订单状态会变,比如从“配送中”变成“已完成”。怎么一次性更新很多记录,而且出错还能回滚?当然要用事务啦。

再来看一个场景:批量更新订单状态。

例子如下:

-- 开始事务
BEGIN;

-- 步骤 1. 更新已过配送日期的订单
UPDATE orders
SET status = '已完成'
WHERE delivery_date < CURRENT_DATE;

-- 步骤 2. 通知更新成功
RAISE NOTICE '所有订单状态已成功更新。';

-- 应用更改
COMMIT;

如果出错了咋办?

总有可能出错。比如你忘了写WHERE条件,结果所有订单状态都变成已完成。为了避免这种事,记得要么提交事务,要么明确回滚。

来看下回滚的场景:

-- 开始事务
BEGIN;

-- 步骤 1. 尝试不加条件更新订单(哎呀,出错了!)
UPDATE orders
SET status = '已完成';

-- 因为出错回滚事务
ROLLBACK;

-- 现在订单都没变

SAVEPOINT加点“灵活性”

有时候你不想回滚整个事务。如果你的场景有好几个步骤,可能只想撤销其中一部分。这时候SAVEPOINT就派上用场了。

现在我们的场景是:处理多个步骤,可以只回滚其中一个。

比如你处理一个订单有好几步:从仓库扣货、更新订单状态、给客户发通知。如果通知没发成功,你只想撤销这一步,但保留数据库里的更改。

-- 开始事务
BEGIN;

-- 步骤 1. 从仓库扣货
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;

-- 保存回滚点
SAVEPOINT step1;

-- 步骤 2. 更新订单状态
UPDATE orders
SET status = '已发货'
WHERE order_id = 202;

-- 尝试给客户发通知
SAVEPOINT step2;
-- 哎呀,通知出错了!
ROLLBACK TO SAVEPOINT step2;

-- 决定安全地提交事务
COMMIT;

总结

事务不仅仅是技术工具,更是你数据完整性的保障。它们能防止“多米诺骨牌效应”,一出错就全崩。每次你要做多个相关操作时,问问自己:“要是其中一个挂了会咋样?”如果答案是“灾难”,那就该用事务了。 记住:写事务多花几分钟,总比出事后花几个小时恢复数据强。你的用户(还有你的心态)都会感谢你的!

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