让我们想象一下,我们正在构建GitHub,我们有两个表:repos
和issues
。每个GitHub仓库都有一系列问题,因此issues
表的外键为repo_id
。
现在,当您浏览GitHub回购问题时,您不希望被曝光到内部id
。相反,您需要像number
这样的东西,它仅从1..n
递增到该存储库。您希望新回购中的第一个问题编号为1
,而不是GitHub上的问题的下一个id
。
当然,您需要一种增量方式,并且您希望确保number
在作用于回购时的唯一性。因此,您特别希望避免任何可以生成两次相同数字的竞争条件。
最直接的处理方法是什么?触发器?还有其他什么呢?
我正在使用PostgreSQL,但更喜欢可能的vanilla SQL方法,例如:触发。如果有一个更简单的Postgres方法,那么这也是有用的。
任何展示您的方法的代码都会非常有用。谢谢!
答案 0 :(得分:1)
我认为如果没有竞争条件的可能性,我有办法做到这一点。这应该最小化竞争条件,但不能消除它们。在特定的数据库体系结构中可能有更好的方法。假设" REPOSITORY_ID"由您的应用程序代码提供:
insert into issues (repo_id,line_id) values (
REPOSITORY_ID,
coalesce((select max(line_id)+1 from issues where repo_id=REPOSITORY_ID),0)
);
这会拉出当前最高的line_id,并在插入时将其递增。如果没有记录,则默认为0.如果两个插入在同一时间点击,则竞争的可能性很小,但似乎不太可能。如果强制使用唯一性,则可以检查插入时是否出错,并在失败时重试。
答案 1 :(得分:1)
假设您要向某个issue
添加新的repo
,您可以执行以下操作:
repo
选择要修改的SELECT ... FOR UPDATE
。这将对其进行行级锁定,并阻止其他想要为issue
添加新repo
的事务同时进行; repo
的新问题编号(例如,您可以在latest_issue
中添加issue
列,就像在其中一个答案中一样,或者您可以执行查询找到它); issue
; repo
上工作的交易继续。因此,您可以通过这种方式定义存储过程,并在每次要插入新的issue
时调用它。假设没有太多并发事务试图为同一个存储库插入新问题,这将阻止竞争条件并仍然以合理的效率运行。
答案 2 :(得分:0)
我会在latest_issue
中保留repos
列,并将其初始化为0
在issues
我要在nr
上创建一个UNIQUE
个约束(repo_id, nr)
,其中repo_id
是issues
中的外键列}。
每当创建问题时,latest_issue
中的repos
都会增加。然后,此号码将用作nr
的{{1}}。
答案 3 :(得分:0)
以下是我将如何处理它:
create table repo(
id serial primary key
);
create table issue(
id integer not null,
id_repo integer not null references repo(id),
primary key (id, id_repo)
);
create function create_issue_seq() returns trigger as $$
begin
execute format('create sequence issue_%s_seq', new.id);
return new;
end
$$ language plpgsql;
create trigger create_issue_seq after insert on repo
for each row execute procedure create_issue_seq();
create function assign_issue_id() returns trigger as $$
begin
new.id = nextval(format('issue_%s_seq', new.id_repo));
return new;
end
$$ language plpgsql;
create trigger assign_issue_id before insert on issue
for each row execute procedure assign_issue_id();
一个触发器在创建repo后创建了问题ID序列(专用于给定的repo),第二个触发器利用现有的专用序列在插入问题之前正确填充问题ID。
优点:
缺点:
注意: