我有一个SaaS宠物项目用于开发票。在其中,我希望我的客户每个都以1001号票号开头。显然,我不能在Postgres中使用简单的自动字段,只需在该值中添加1000,因为我的所有客户端将共享相同的数据库和相同的{{ 1}} table ..我尝试使用整数列类型并查询(伪SQL)tickets
以获取最新数字,然后使用该数字SELECT LATEST number FROM tickets WHERE client_id = [current client ID]
获取下一个{{1} }。问题在于,通过并发,两张票很容易以这种方式以相同的数字结尾。我需要能够在Django中或使用原始SQL(使用Bash或其他任何类型的东西)执行此操作的数字。
我不是在寻找一种方法来强迫我的例子发挥作用。我只是在寻找解决我需要为每个客户单独增加票号的问题。
答案 0 :(得分:5)
我不认为这个问题有“便宜”的解决方案。在多用户环境中唯一安全(但不一定快)的解决方案是为每个客户提供一个“计数器”表,其中包含一行。
在插入新票证之前,每个交易都必须首先锁定客户的条目,如下所示:
UPDATE cust_numbers
SET current_number = current_number + 1
WHERE cust_id = 42
RETURNING current_number;
这将一步完成三件事
使用该新号码,您现在可以插入新票证。如果事务已提交,它还将释放cust_numbers
表上的锁,因此可以继续“等待数字”的其他事务。
您可以将这两个步骤(更新...返回和插入)包装到单个存储函数中,以便将此背后的逻辑集中在一起。您的应用程序只会在不知道票号如何生成的情况下调用select insert_ticket(...)
。
您可能还希望在customer表上创建一个触发器,以便在创建新客户时自动将行插入cust_numbers
表。
这样做的缺点是您可以有效地序列化为同一客户插入新票证的交易。根据系统中插入的大小,这可能会成为性能问题。
修改强>
这样做的另一个缺点是,您不是强制以可能导致问题的方式插入票证,例如,一位新的开发人员忘了这件事。
答案 1 :(得分:2)
您可以为每个客户创建序列,然后将列的值设置为nextval('name_of_the_sequence')
。这实际上是serial
的工作原理;您的情况唯一的区别是您不使用列的默认值并且具有多个序列。
在插入新行时创建那些选择正确序列的序列可以通过PL / Pgsql过程很好地完成。