Tsql T-SQL中的序列生成器

Tsql T-SQL中的序列生成器,tsql,sequence,Tsql,Sequence,我们有一个Oracle应用程序,它使用标准模式填充代理键。我们有一系列外部行(具有代理键的特定值)和其他具有内部值的行。 我们使用以下Oracle触发器片段来确定在插入时如何处理代理键: IF :NEW.SurrogateKey IS NULL THEN SELECT SurrogateKey_SEQ.NEXTVAL INTO :NEW.SurrogateKey FROM DUAL; END IF; 如果提供的代理项为null,则从指定序列中获取一个值,否则将提供的代理项传递到行 我似乎

我们有一个Oracle应用程序,它使用标准模式填充代理键。我们有一系列外部行(具有代理键的特定值)和其他具有内部值的行。 我们使用以下Oracle触发器片段来确定在插入时如何处理代理键:

IF :NEW.SurrogateKey IS NULL THEN

 SELECT SurrogateKey_SEQ.NEXTVAL INTO :NEW.SurrogateKey FROM DUAL;

END IF;
如果提供的代理项为null,则从指定序列中获取一个值,否则将提供的代理项传递到行

我似乎找不到一个简单的方法来实现这一点,这就是t-SQL。有各种各样的方法,但没有一种像Oracle和其他符合SQL-92的DBs那样使用序列生成器的概念


有人知道在SQLServerT-SQL中实现这一点的真正有效方法吗?顺便说一下,如果有帮助的话,我们使用的是SQL Server 2008。

您可能需要查看IDENTITY。这将为您提供一列,插入行时将确定该列的值

这可能意味着您必须插入行,然后使用SCOPE_IDENTITY()确定值


这里还有一篇关于在SQL Server中模拟Oracle序列的文章:

标识是一种方法,尽管它将在每个表级别生成唯一标识符

另一种方法是使用唯一标识符,特别是使用NewSequantialID()生成的id总是大于最后一个id。这种方法的问题是不再处理整数


模拟oracle方法的最接近的方法是使用一个带有计数器字段的单独表,然后编写一个用户定义的函数来查询此字段,对其进行递增并返回值。

下面是一种使用表存储上一个序列号的方法。存储过程非常简单,其中的大部分内容都是因为我很懒,不喜欢在我忘记某件事情时出现意外情况,所以…这里是:

-----创建序列值表

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[SequenceTbl]
(
  [CurrentValue] [bigint]
) ON [PRIMARY]

GO

-----------------创建存储过程

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE procedure [dbo].[sp_NextInSequence](@SkipCount BigInt = 1)
AS

BEGIN

  BEGIN TRANSACTION

    DECLARE @NextInSequence BigInt;

    IF NOT EXISTS
    (
      SELECT
        CurrentValue
      FROM
        SequenceTbl
    )

    INSERT INTO SequenceTbl (CurrentValue) VALUES (0);

    SELECT TOP 1
      @NextInSequence = ISNULL(CurrentValue, 0) + 1
    FROM
      SequenceTbl WITH (HoldLock);

    UPDATE SequenceTbl WITH (UPDLOCK)
      SET CurrentValue = @NextInSequence + (@SkipCount - 1);

  COMMIT TRANSACTION

  RETURN @NextInSequence
END;
GO

--------使用Sql Manager中的存储过程检索测试值

declare @NextInSequence BigInt

exec @NextInSequence = sp_NextInSequence;

--exec @NextInSequence = sp_NextInSequence <skipcount>;

select NextInSequence = @NextInSequence;
精明的人会注意到存储的进程有一个参数(可选)。这是为了允许调用者在调用者有多条记录需要唯一ID的情况下保留一个ID块-使用SkipCount,调用者只需对需要的ID进行一次调用。 如果在创建表时记得插入记录,则可以删除整个“IF EXISTS…INSERT INTO…”块。如果您还记得用一个值(您的种子值-一个永远不会用作ID的数字)插入该记录,那么您还可以删除select的ISNULL(…)部分,并仅使用CurrentValue+1。
现在,在任何人发表评论之前,请注意我是一名软件工程师,而不是dba!因此,任何关于使用“Top 1”、“With(HoldLock)”和“With(UPDLock)”的建设性批评都是受欢迎的。我不知道这会扩展到什么程度,但到目前为止,这对我来说还可以…

谢谢Matthieu,我知道表的IDENTITY属性,但我正试图避免它,因为我希望保留我们目前拥有的灵活性。另外,您所指的文章使用了额外的表和查找,这是我试图避免的。您还需要为每个“序列”创建一个表;但是有一个特殊的要求,代理键必须是整数。据我所知,unque标识符是guid或uuid。请参阅建设性的批评:)不要玩那些锁,你会受伤的。。。为了避免死锁,我建议使用标识为(1,1)的表,第一个操作将是insert(insert SEQUENCE TBL默认值),您将获得scope_identity(),第二个操作是删除等于该数字的行。这可以通过行级锁定进行序列化。
select * from SequenceTbl;