Skip to content

搞英语 → 看世界

翻译英文优质信息和名人推特

Menu
  • 首页
  • 作者列表
  • 独立博客
  • 专业媒体
  • 名人推特
  • 邮件列表
  • 关于本站
Menu

数据库触发器重新计算总数,以确保数据完整性

Posted on 2022-03-09

当您创建一个数据库支持的应用程序时,您编写代码以确保相关字段中的数据完整性。

如果一件商品已售出,您将更新库存。如果有人取款,您会更新他们的余额。每当有人进行更改时,您都会重新计算总计并更新数据库中的相关字段。

通常这是在您的主代码中完成的:您的 JavaScript、Python、Ruby、Java 或其他代码。

但是,如果某些未来的代码不使用您当前至关重要的功能怎么办?新的后端接口、API 挂钩或简单的 shell 脚本可能不会使用您当前的代码。新代码将直接访问数据库,而不是使用旧代码。

我在上一家公司遇到过这种情况,并且感到库存甚至财务出现问题的痛苦,这一切都是因为一些新代码正在直接访问数据库。

所以这就是为什么我现在成为一名传道者,因为将您的关键代码放入数据库本身是多么重要。这是数据逻辑(不是“业务逻辑”),应该绑定到数据。可以触发数据库功能,确保完整性,无论外部代码访问它。

这是 PostgreSQL 中的一个示例,使用购物车:

首先,制作三个简单的表格:

  1. 有价格的物品。
  2. 带有数量的订单项。
  3. 带有总价的发票。

创建两个示例项目,一个 5 美元和一个 9 美元的项目。并创建发票#1 进行测试。

 create table items ( id serial primary key, price int not null check (price > 0) ); create table invoices ( id serial primary key, total int ); create table lineitems ( invoice_id int not null references invoices(id), item_id int not null references items(id), quantity int not null check (quantity > 0), primary key (invoice_id, item_id) ); -- example data: insert into items (price) values (5); insert into items (price) values (9); insert into invoices (total) values (0);

下载代码

现在,您希望它在更改 Lineitems 时重新计算发票总额。那是一个叫做触发器的特殊函数。首先创建函数,然后创建执行函数的触发器。

 -- re-calculate the total of a lineitem's invoice create function recalc() returns trigger as $$ begin -- update invoice using lineitems's invoice_id update invoices set total = ( select sum(quantity * price) from lineitems join items on lineitems.item_id = items.id where invoice_id = new.invoice_id) where id = new.invoice_id; return new; end; $$ language plpgsql; -- run this function after any change to lineitems create trigger recalc after insert or update or delete on lineitems for each row execute procedure recalc();

下载代码

通过添加 Lineitems 并更新它们的数量来测试它。每次更改后,查看发票。有用!

 insert into lineitems (invoice_id, item_id, quantity) values (1, 1, 1); insert into lineitems (invoice_id, item_id, quantity) values (1, 2, 1); select * from lineitems; select * from invoices; select 'Notice new total when you update quantity:' look; update lineitems set quantity = 5 where invoice_id = 1 and item_id = 2; select * from lineitems; select * from invoices; select 'But when you delete a line? Oh no. Total is still $50' look; delete from lineitems where invoice_id = 1 and item_id = 2; select * from lineitems; select * from invoices; -- Let's try again in recalc-4.sql drop function recalc() cascade;

下载代码

但请注意,如果您删除 Lineitem,它不会更新 Invoice!我勒个去?为什么不?

啊,那是因为该函数引用了一个名为“new”的记录变量,由 Lineitems 上的触发器传递,并使用它来获取 invoice_id。但是当你删除一个 Lineitem 时,记录变量被称为“old”,因为它引用了一个已经被删除的 Lineitem。该死。

您可以制作两个单独的触发器,一个使用“new”,只在更新和插入操作时调用,另一个使用“old”,只在删除时调用。但是你会重复总数的计算。因此,只需添加一个丑陋的 if/then 来将“new”或“old”分配给名为“r”的变量,并使用“r”代替。这是更新的功能:

 -- re-calculate the total of a lineitem's invoice create function recalc() returns trigger as $$ declare r record; begin -- use "new" lineitems record for insert/update, or "old" if delete if (tg_op = 'DELETE') then r = old; else r = new; end if; -- update invoice using lineitems(now "r")'s invoice_id update invoices set total = ( select sum(quantity * price) from lineitems join items on lineitems.item_id = items.id where invoice_id = r.invoice_id) where id = r.invoice_id; -- must return incoming "new" or "old" record when done return r; end; $$ language plpgsql; -- run this function after any change to lineitems create trigger recalc after insert or update or delete on lineitems for each row execute procedure recalc();

下载代码

现在测试添加、更新和删除,注意它们都会更新 Invoice 总计:

 insert into lineitems (invoice_id, item_id, quantity) values (1, 2, 1); select * from lineitems; select * from invoices; select 'Notice new total when you update quantity:' look; update lineitems set quantity = 5 where invoice_id = 1 and item_id = 2; select * from lineitems; select * from invoices; select 'Notice new total when you delete:' look; delete from lineitems where invoice_id = 1 and item_id = 2; select * from lineitems; select * from invoices;

下载代码

凉爽的?您会获得更多的安全性和安心,因为您知道无论什么代码更新您的数据库,相关字段都会自动更新。

在此处下载最终示例文件: /code/recalc.sql 。

在你让这个示例运行之后,让我们尝试另一个将数据逻辑放入数据库的示例,方法是创建用于更新购物车的函数。

我也使用触发器来清理数据,但这是我很快会写的另一个主题。

来源: https://sive.rs/recalc

发表回复 取消回复

要发表评论,您必须先登录。

本站文章系自动翻译,站长会周期检查,如果有不当内容,请点此留言,非常感谢。
  • Abhinav
  • Abigail Pain
  • Adam Fortuna
  • Alberto Gallego
  • Alex Wlchan
  • Answer.AI
  • Arne Bahlo
  • Ben Carlson
  • Ben Kuhn
  • Bert Hubert
  • Bits about Money
  • Brian Krebs
  • ByteByteGo
  • Chip Huyen
  • Chips and Cheese
  • Cool Infographics
  • Dan Sinker
  • David Walsh
  • Dmitry Dolzhenko
  • Elad Gil
  • Ellie Huxtable
  • Ethan Marcotte
  • Exponential View
  • FAIL Blog
  • Founder Weekly
  • Geoffrey Huntley
  • Geoffrey Litt
  • Greg Mankiw
  • Henrique Dias
  • Hypercritical
  • IEEE Spectrum
  • Investment Talk
  • Jaz
  • Jeff Geerling
  • Jonas Hietala
  • Josh Comeau
  • Lenny Rachitsky
  • Lou Plummer
  • Luke Wroblewski
  • Matt Stoller
  • Mert Bulan
  • Mostly metrics
  • News Letter
  • NextDraft
  • Non_Interactive
  • Not Boring
  • One Useful Thing
  • Phil Eaton
  • Product Market Fit
  • Readwise
  • ReedyBear
  • Robert Heaton
  • Ruben Schade
  • Sage Economics
  • Sam Altman
  • Sam Rose
  • selfh.st
  • Shtetl-Optimized
  • Simon schreibt
  • Slashdot
  • Small Good Things
  • Taylor Troesh
  • Telegram Blog
  • The Macro Compass
  • The Pomp Letter
  • thesephist
  • Thinking Deep & Wide
  • Tim Kellogg
  • 英文媒体
  • 英文推特
  • 英文独立博客
©2025 搞英语 → 看世界 | Design: Newspaperly WordPress Theme