mariadb 如果选择查询返回0行,则插入到表中

lxkprmvk  于 2023-05-06  发布在  其他
关注(0)|答案(3)|浏览(151)

我想在数据库表中插入一个新行,前提是事先满足一个条件。

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的新手,对这个问题挠头已经有一段时间了。如果你们中的任何一个聪明的人能帮助我找到一个解决方案,我将永远感激。

yxyvkwin

yxyvkwin1#

添加一个派生的、唯一约束的列,该列跟踪两个用户的唯一组合“user 1-user 2”,其中user 1是“最低”用户,user 2是“最高”用户:

create table mytable (
  sender_id text,
  receiver_id text,
  col3 text, 
  combo_key text not null
    default concat(least(sender_id, receiver_id), '-', greatest(sender_id, receiver_id))
    unique
)

参见live demo
尝试插入逻辑重复项将引发“重复项”异常。
破折号(可以是任何东西)被添加在用户字符串之间,以区分用户对“abc”和“de”与“ab”和“cde”。

hxzsmxv2

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自动进行重新排序(更健壮)。因为您正在重新排序参数,所以不再清楚发送者和接收者是谁。您可能希望将列重命名为更通用的名称,如user1user2
最后,使用on duplicate key update设置第三列(如果您需要的话)。

insert into relationships (sender_id, receiver_id, col3) values
  (
   if('user1' < 'user2', 'user1', 'user2'),
   if('user1' > 'user2', 'user1', 'user2'),
   true
  )
on duplicate key update col3=true

Demonstration

juzqafwq

juzqafwq3#

冒号(:)只是一个参数的标记,无论您打算如何提供它们。

insert into T (sender_id, receiver_id, col3)
select :sender, :receiver, true
where not exists (
    select 1 from T
    where    sender_id = :sender and receiver_id = :receiver
        or receiver_id = :sender and   sender_id = :receiver
    )

相关问题