SQL Server 2005 - 使用生成的序列而不是Identity列?

时间:2011-02-22 20:54:02

标签: sql-server-2005 identity sequence

我真的在考虑切换SQL Server 2005中的Identity列并使用某种类型的序列生成器(据我所知,2005年没有内置任何内容;可能会在2011年推出?)到获取插入行的唯一ID。

是否有模型实施或最佳实践?我会遇到锁定问题吗?与仅使用标识列相比,有哪些缺点?

2 个答案:

答案 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;
}