我有这个TSQL代码,用于检查'sadsadsad'是否存在,如果没有,则将其插入表中。
if not exists(select id from [ua_subset_composite] where ua = 'sadsadsad')
begin
insert into [ua_subset_composite]
select 'sadsadsad',1,null,null,null,null
end
我担心的是,在生产中会同时运行多个线程,可能会出现记录在不存在的select和insert之间滑动的情况。
我不想在列上添加唯一约束,并想知道我是否可以改进此SQL代码以确保它的唯一性
答案 0 :(得分:2)
解决此问题的一种方法是使用更高级别的隔离(即锁定)。您可以将整个语句包装在事务中,并使用更严格的隔离级别。
例如:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION
<your code here>
COMMIT TRANSACTION
答案 1 :(得分:0)
您可以在数据库上实施锁定策略。你可以选择悲观:
当您锁定记录供您独家使用时,直到您拥有 完成它。它具有比乐观锁定更好的完整性 但要求您小心应用程序设计以避免 死锁。
或乐观:
您在那里阅读记录,记下版本号并检查 在您将记录写回之前,版本没有更改。当你 写回记录,过滤更新版本 确定它是原子的。 (即检查时间之间没有更新 版本并将记录写入磁盘)并更新版本 一击。
如果记录是脏的(即你的版本不同),你就中止了 交易和用户可以重新启动它。
答案 2 :(得分:0)
当您执行select
时,请在所选范围内放置updlock
,holdlock
:
begin transaction
if not exists(
select id
from [ua_subset_composite] with (updlock, holdlock)
where ua = 'sadsadsad')
begin
insert into [ua_subset_composite]
select 'sadsadsad',1,null,null,null,null
end
commit
holdlock
,相当于serializable
isolation level,具有以下效果:
语句无法读取已修改但尚未由其他交易提交的数据。
在当前交易完成之前,没有其他交易可以修改当前交易所读取的数据。
其他事务无法插入新行,其键值将落在由任何语句读取的键范围内 直到当前交易完成的当前交易。
范围锁定位于与之匹配的键值范围内 搜索事务中执行的每个语句的条件。这个 阻止其他事务更新或插入任何行 有资格获得当前执行的任何陈述 交易。这意味着如果事务中的任何语句 第二次执行,他们将读取相同的行集。该 范围锁保持到事务完成。这是最多的 限制隔离级别因为它锁定了整个范围 密钥并保持锁定,直到事务完成。的因为 并发性较低,仅在必要时使用此选项。
除updlock
之外还需要holdlock
...添加updlock
我们会阻止单独的进程在同一范围内执行自己的select with (updlock, holdlock)
语句在同一时间。
答案 3 :(得分:0)
这就是我最终做的事情
insert into [ua_subset_composite] WITH (TABLOCKX) (ua, os)
select @r, 1
where not exists (select 1 from [ua_subset_composite] nolock where ua = @r
为了测试代码,我从多个窗口同时运行了这段代码
declare @r nvarchar(30);
while(1=1)
begin
set @r = convert(nvarchar(30),getdate(),21 )
insert into [ua_subset_test] WITH (TABLOCKX) (ua, os)
select @r, 1
where not exists (select 1 from [ua_subset_test] nolock where ua = @r
)
end
答案 4 :(得分:-1)
不幸的是,上述答案都不正确。请注意启动BEGIN TRAN SELECT
的任何“锁定”解决方案。是的,如果隔离级别是SERIALIZABLE,SELECT
会创建阻止其他进程更新所选数据的锁。但是如果没有选择数据呢?锁定什么?
IOW,BEGIN TRAN
设置竞争条件:
/* spid */
/* 1 */ SELECT ... -- returns no rows
/* 2 */ SELECT ... -- returns no rows
/* 1 */ INSERT ... -- whew!
/* 2 */ INSERT ... -- error
要在写入之前阅读(比如向用户显示数据),有特殊的时间戳数据类型。但在你的情况下,它只是一个插入。使用原子事务,即单个语句:
insert into [ua_subset_composite] (column1, column2)
values ('sadsadsad', 1)
where not exists (
select 1 from ua_subset_composite
where column1 = 'sadsadsad'
)
服务器保证行插入或未插入。锁定是为您完成的,需要在最短的时间内由知道如何操作的人完成。 : - )
我不想添加唯一约束
嗯,你可能应该,你知道。上面的代码将阻止尝试添加非唯一值,并避免出现错误消息。一个独特的约束将阻止某人不小心成功。