4

我有一个名为 as 的表'games',其中包含一个名为 as 的列'title',该列是唯一的,数据库用于PostgreSQL

我有一个用户输入表单,允许他'game''games'表中插入一个新表单。插入新游戏的功能会检查先前输入'game'的相同游戏是否'title'已经存在,为此,我得到count of rows, 与相同的游戏'title'

我为此使用事务,开始时的插入函数使用BEGIN,获取行数,如果行数为0,则插入新行,处理完成后,它COMMITS会发生变化。

问题是,title如果用户同时提交 2 个相同的游戏,有可能会被插入两次,因为我只是获取行数来检查重复记录,并且每个事务都是彼此隔离

我想在获取行数时锁定表:

LOCK TABLE games IN ACCESS EXCLUSIVE MODE;
SELECT count(id) FROM games WHERE games.title = 'new_game_title' 

这也会锁定表以供读取(这意味着另一个事务必须等待,直到当前事务成功完成)。这将解决问题,这是我怀疑的。有没有更好的解决方法(避免games重复title

4

2 回答 2

4

在这种情况下,您不需要锁定您的表。

相反,您可以使用以下方法之一:

  • UNIQUE为必须唯一的列定义索引。在这种情况下,第一个事务将成功,第二个事务将出错。
  • 定义AFTER INSERT OR UPDATE OR DELETE将检查您的条件的触发器,如果​​它不成立,它应该RAISE出错,这将中止违规交易

在所有这些情况下,您的客户端代码应该准备好正确处理可能通过执行语句返回的可能失败(如失败的事务)。

于 2013-01-06T09:06:48.093 回答
3

使用最高的事务隔离(Serializable),您可以实现与您的实际问题类似的事情。但请注意,这可能会失败ERROR: could not serialize access due to concurrent update

我完全不同意约束方法。您应该有一个约束来保护数据完整性,但是依赖于约束会迫使您不仅要识别发生了什么错误,还要识别是哪个约束导致了错误。问题不是像某些人讨论的那样捕获错误,而是确定导致错误的原因并提供人类可读的失败原因。根据您的应用程序是用哪种语言编写的,这几乎是不可能的。例如:告诉用户“游戏标题 [foo] 已经存在”而不是“游戏必须有价格”以获得单独的约束。

您的两阶段方法有一个单一的语句替代方案:

INSERT INTO games ( [column1], ... )
SELECT [value1], ...
WHERE NOT EXISTS ( SELECT x FROM games as g2 WHERE games.title = g2.title );

I want to be clear with this... this is not an alternative to having a unique constraint (which requires extra data for the index). You must have one to protect your data from corruption.

于 2013-01-06T12:32:31.177 回答