我正在通过网络对数据库执行插入操作,并且我想确保我永远不会尝试插入我已经插入的数据。客户端在 Web 浏览器中运行,使用 Javascript 将 XMLHttpRequest 发送到包含要插入的数据的服务器。如果出现错误,那么我必须稍后再试 - 例如,可能存在暂时的网络错误。但是,我无法保证我会收到插入成功的通知,因为在请求通过服务器后但在返回成功回复之前网络可能会关闭。尽管这对我来说似乎不太可能,但如果发生这种情况,它将完全弄乱数据库,因为无法知道某些条目不应该存在。
2 回答
使用您正在插入的数据的存在或不存在,这样如果第二个相同的事务通过,它就会被忽略。就像是:
INSERT MyTable (Value1, Value2, Value3)
SELECT @Value1, @Value2, @Value3
WHERE
NOT EXISTS (
SELECT *
FROM dbo.MyTable T
WHERE
T.Value1 = @Value1
AND T.Value2 = @Value2
)
;
请注意,这并不能完全解决两个客户端尝试同时执行相同操作的潜在竞争条件——该EXISTS
子句在INSERT
操作之前及时执行,这可能会产生两个尝试插入的客户端。因此,您必须添加锁定,例如WITH (UPDLOCK, HOLDLOCK)
(对于并发性很糟糕,因为如果该行不存在,它必须采用可以阻止不相关数据插入的范围锁定),或者在客户端中适当地容忍有关重复键的错误。
如果只要客户端发送数据就可以复制数据,那么您可以通过以下几种方式进行调整:
向表中添加其他列,直到它是唯一的。
或者,创建作为事务一部分的时间戳/序列号,并在插入后(在数据库事务中)将此序列号记录到表中。将插件与缺少此序列号相关联。
为您的客户端制定一些策略,直到它们可以被确认为止,这可能是一个额外的保护层,同时可能会首先防止一些重复尝试——如果您现在没有得到确认,但它稍后会到达,您确认/删除排队发送的交易。
要将此概括为防止重复DELETE
and UPDATE
,您应该添加一个rowversion 类型的列。将此值包含在WHERE
所有查询的子句中,如果自您上次阅读该行以来其他人已触及该行,这些查询将不会影响任何行。您仍然必须检测到没有行受到影响并在之后执行一些智能操作——这可能非常困难——但至少您已经检测到并避免了编辑冲突或重复操作。
如果问题不能与特定行的特定编辑隔离,那么请提出一个新问题,提供一些具体示例,以便您获得更好的建议。给我一个带有新问题链接的评论,我会看看。
尝试向您的字段添加一些约束:
ALTER TABLE Persons ADD CONSTRAINT MyTable UNIQUE (Value1, Value2, Value3)