我真的在考虑切换SQL Server 2005中的Identity列并使用某种类型的序列生成器(据我所知,2005年没有内置任何内容;可能会在2011年推出?)到获取插入行的唯一ID。
是否有模型实施或最佳实践?我会遇到锁定问题吗?与仅使用标识列相比,有哪些缺点?
答案 0 :(得分:5)
是的,SQL 11有SEQUENCE对象,请参阅SQL Server v.Next (Denali) : Using SEQUENCE。
可以创建手动序列,但不建议这样做。执行序列生成器的技巧是在序列表上使用UPDATE WITH OUTPUT。这是伪代码:
CREATE TABLE Sequences (
Name sysname not null primary key,
Sequence bigint not null default 0);
GO
CREATE PROCEDURE sp_getSequence
@name sysname,
@value bigint output
AS
UPDATE Sequences
SET Sequence = Sequence + 1
OUTPUT @value = INSERTED.Sequence
WHERE Name = @name;
GO
我遗漏了一些细节,但这是一般的想法。但是,存在一个巨大的问题:任何请求序列上的下一个值的事务都会锁定该序列,直到它提交为止,因为它会对序列值进行更新锁定。这意味着所有事务在插入值时必须相互序列化,并且在实际生产部署中导致性能降低是无法忍受的。
我宁愿让你坚持使用IDENTITY类型。虽然不完美,但它们远比你自己实现的要好得多。
答案 1 :(得分:1)
我用来解决这个问题的方法是一个表'Sequences',它存储我的所有序列和'nextval'存储过程。
Sql表:
CREATE TABLE Sequences (
name VARCHAR(30) NOT NULL,
value BIGINT DEFAULT 0 NOT NULL,
CONSTRAINT PK_Sequences PRIMARY KEY (name)
);
PK_Sequences 仅用于确保永远不会有具有相同名称的序列。
Sql存储过程:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'nextVal') AND type in (N'P', N'PC')) DROP PROCEDURE nextVal;
GO
CREATE PROCEDURE nextval
@name VARCHAR(30)
AS
BEGIN
DECLARE @value BIGINT
BEGIN TRANSACTION
UPDATE Sequences
SET @value=value=value + 1
WHERE name = @name;
-- SELECT @value=value FROM Sequences WHERE name=@name
COMMIT TRANSACTION
SELECT @value AS nextval
END;
插入一些序列:
INSERT INTO Sequences(name, value) VALUES ('SEQ_Workshop', 0);
INSERT INTO Sequences(name, value) VALUES ('SEQ_Participant', 0);
INSERT INTO Sequences(name, value) VALUES ('SEQ_Invoice', 0);
最后得到一个序列的下一个值,
execute nextval 'SEQ_Participant';
一些c#代码从Sequence表中获取下一个值,
public long getNextVal()
{
long nextval = -1;
SqlConnection connection = new SqlConnection("your connection string");
try
{
// Connect and execute the select sql command.
connection.Open();
SqlCommand command = new SqlCommand("nextval", connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@name", SqlDbType.NVarChar).Value = "SEQ_Participant";
nextval = Int64.Parse(command.ExecuteScalar().ToString());
command.Dispose();
}
catch (Exception) { }
finally
{
connection.Dispose();
}
return nextval;
}