如果我有以下内容:
Begin transaction
numberOfRecords = select count from table where foreignKey = "some value"
Insert into table (set SequenceNumber = numberOfRecords + 1)
End Transaction
并且多个用户正在执行上述代码,每个插入都会有一个唯一的增加数字吗?
换句话说,begin事务是否排队其他事务甚至读取,以便每个插入都具有正确的序列号?或者我是否需要Insert into..Select语句来实现我想要的目标?
感谢。
答案 0 :(得分:2)
不,具有默认SQL Server隔离级别(READ COMMITTED)的事务是不够的。将它放入一个INSERT...SELECT
语句也不会解决它。你基本上有两种方法可以解决这个问题:
选项1 :将隔离级别设置为SERIALIZABLE:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
。这将确保来自两个不同用户的事务发生,就像它们按顺序发生一样。但是,如果许多此类事务并行发生,则可能会造成死锁。
选项2 :在事务开始时(SELECT * FROM table WITH (TABLOCKX, HOLDLOCK) WHERE 1=0
)独占锁定表。请注意,如果经常使用table
,这可能会影响性能。
我对以下SO问题的回答详细分析了这两种方法:
答案 1 :(得分:1)
不,事务不排队命令,它不像锁。
通常你想使用一个标识列,但在某些情况下,如果你想生成没有间隙的SequenceNumber,你需要在SequenceNumber列上使用上面的代码和唯一约束,并准备在提交事务抛出一个时重试异常。
答案 2 :(得分:1)
我曾经使用SQL DB作为大规模数据导出的记录器,为了获得顺序“身份”,我创建了一个“on insert”触发器,用于发出下一个数字。 对我来说工作得很好,但它只是一个用户数据库,因此不确定是否存在多个用户和我所做的问题。
现在我已经重新阅读了这个问题,这可能不是你想要的,但我认为你也可以为选择做一个触发器?
USE [ExportLog]
GO
/****** Object: Trigger [dbo].[Migration_Update_Event_Date] Script Date: 02/10/2011 17:06:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[Migration_Update_LogState]
ON [dbo].[MigrationLog] FOR INSERT NOT FOR REPLICATION
AS
UPDATE [MIGRATION-DATA].dbo.MIGRATIONSTATE
SET LASTPROCESSID = ID
WHERE MACHINENAME IN (SELECT MACHINENAME FROM INSERTED)
GO
答案 3 :(得分:1)
如果你想以唯一的方式插入记录,那么首先你要按顺序创建记录,然后记录应插入序列。似乎
create sequnce seq_num ;
现在使用seq_num
插入rcords。
insert into <table name>(col1) values(seq_num.nextval);
答案 4 :(得分:1)
您需要将事务隔离级别设置为提供正确隔离级别的级别。在您的代码中,两个进程可以执行第一行,然后执行第二行。两者都会插入相同的值。
将隔离级别设置为可序列化并执行select语句WITH (UPDLOCK)
。这将减少系统中的并发性,但它将是安全的。
其他策略是可行的,但实施(并测试!)会更耗时。