我想在数据库表中插入一个新行,前提是事先满足一个条件。
ContextMariaDB数据库,10.3.38-MariaDB-0ubuntu0.20.04.1
这是我的table:
+-----------+-------------+------+
| sender_id | receiver_id | col3 |
+-----------+-------------+------+
其中sender_id和receiver_id都是16字节外键列,col 3是布尔值。
- 此表跟踪两个用户之间的关系,id列表示一个16字节的UUID。
- 用户可以请求在表中插入自己与另一个用户之间的新关系。
- 发起关系的用户的uuid放在列中:sender_id,他们想要绑定的用户的uuid放在列中:接收器ID
问题每当用户尝试创建新关系时,数据库必须检查他们是否已经有关系,并确保不存在在列之间交换其uuid值的另一个关系。
例如:如果数据库是这样的,
+-----------+-------------+------+
| sender_id | receiver_id | col3 |
+-----------+-------------+------+
| user 1 | user 2 | true |
+-----------+-------------+------+
假设用户2尝试发起与用户1的新关系。在这种情况下,数据库看起来像这样。
+-----------+-------------+------+
| sender_id | receiver_id | col3 |
+-----------+-------------+------+
| user 1 | user 2 | true |
+-----------+-------------+------+
| user 2 | user 1 | true |
+-----------+-------------+------+
如何防止用户插入新行,即使在本例中插入新行在技术上有所不同?
我尝试了什么我尝试使用一个IF语句和一个SELECT查询来检查是否存在任何与我的参数匹配的行,如果不存在,则插入该行;但是它抛出了一个SQL错误。
限制我希望使用尽可能少的SQL语句。优选地,应当仅发送单个语句。
我是SQL的新手,对这个问题挠头已经有一段时间了。如果你们中的任何一个聪明的人能帮助我找到一个解决方案,我将永远感激。
3条答案
按热度按时间yxyvkwin1#
添加一个派生的、唯一约束的列,该列跟踪两个用户的唯一组合“user 1-user 2”,其中user 1是“最低”用户,user 2是“最高”用户:
参见live demo。
尝试插入逻辑重复项将引发“重复项”异常。
破折号(可以是任何东西)被添加在用户字符串之间,以区分用户对“abc”和“de”与“ab”和“cde”。
hxzsmxv22#
选择和插入基于选择的结果是一种“检查和做”模式。如果两个用户试图同时选择和插入,则race conditions会打开此选项。他们都检查了,发现没有行,然后都尝试插入:一个会失败。
您可以通过事务和
select for update
之类的东西来解决这个问题,但这涉及到仔细的编码和可能导致死锁的锁。最好是“先做后查”一步到位。这是"atomic",意味着它是一个不能被中断的单一操作。
1.设置约束,以便无法添加重复项。
1.做插入。
1.如果存在冲突,则仅更新第3列。
有几种方法可以设置约束。
最简单和最健壮的是使用两个唯一索引,一个在
(sender_id, receiver_id)
上,另一个在(receiver_id, sender_id)
上。这涵盖了两种情况。这使您可以保留发送者/接收者顺序。另一种是在
(sender_id, receiver_id)
上使用一个唯一的索引,并添加一个constraint,要求sender_id总是小于receiver_id(反之亦然):check(sender_id < receiver_id)
。这也确保它们不相同。然后在插入逻辑中,对参数重新排序,以便始终以正确的顺序插入用户。您可以在insert语句中使用if()(更简单),或者使用before insert and before update triggers自动进行重新排序(更健壮)。因为您正在重新排序参数,所以不再清楚发送者和接收者是谁。您可能希望将列重命名为更通用的名称,如user1
和user2
。最后,使用on duplicate key update设置第三列(如果您需要的话)。
Demonstration。
juzqafwq3#
冒号(:)只是一个参数的标记,无论您打算如何提供它们。