14

在 Oracle 中有一种生成序列号的机制,例如;

CREATE SEQUENCE supplier_seq

    MINVALUE 1
    MAXVALUE 999999999999999999999999999
    START WITH 1
    INCREMENT BY 1
    CACHE 20;

然后执行语句

supplier_seq.nextval

检索下一个序列号。

您将如何在 MS SQL Server 中创建相同的功能?

编辑:我不是在寻找自动为表记录生成键的方法。我需要生成一个唯一值,我可以将其用作进程的(逻辑)ID。所以我需要 Oracle 提供的确切功能。

4

10 回答 10

15

没有完全匹配。

等效的是 IDENTITY,您可以在创建表时将其设置为数据类型。SQLSERVER 将在插入期间自动创建一个正在运行的序列号。最后插入的值可以通过调用 SCOPE_IDENTITY() 或通过查询系统变量@@IDENTITY 获得(正如 Frans 所指出的)

如果您需要精确的等价物,则需要创建一个表,然后编写一个过程来重新调整下一个值和其他操作。请参阅 Marks 对此问题的回应。

编辑:
SQL Server 已经实现了类似于 Oracle 的序列。有关详细信息,请参阅此问题。

您将如何在 Microsoft SQL Server 中实现序列?

于 2009-03-19T12:24:31.593 回答
6

标识是最好和最可扩展的解决方案,但是,如果您需要一个不是递增 int 的序列,如 00A、00B、00C 或某些特殊序列,则有一种次优方法。如果实施正确,它可以扩展,但如果实施不当,它的扩展性很差。我毫不犹豫地推荐它,但你要做的是:

  1. 您必须将“下一个值”存储在表中。该表可以是仅具有该值的简单的一行一列的表。如果您有多个序列,它们可以共享表,但是通过为每个序列设置单独的表,您可能会减少争用。
  2. 您需要编写一个更新语句,将该值增加 1 个间隔。您可以将更新放在存储过程中,以使其易于使用并防止在不同位置的代码中重复更新。
  3. 正确使用序列,以便它可以合理地扩展(不,不如 Identitiy :-) 需要两件事:update 语句具有针对这个确切问题的特殊语法,它将在单个语句中增加和返回值;湾。您必须在事务开始之前和事务范围之外从自定义序列中获取值。这就是 Identity 可扩展的原因之一——无论事务范围如何,对于任何尝试的插入,它都会返回一个新值,但不会在失败时回滚。这意味着它不会阻塞,也意味着您将有失败交易的空白。

特殊的更新语法因版本而异,但要点是您对变量进行赋值并在同一语句中进行更新。对于 2008 年,Itzik Ben-Gan 有这个巧妙的解决方案:http ://www.sqlmag.com/Articles/ArticleID/101339/101339.html?Ad=1

老式 2000 及更高版本的方法如下所示:

UPDATE SequenceTable SET @localVar = value = value + 5 -- 将尾部更改为您的增量逻辑

这将增加并返回下一个值。

如果您绝对不能有间隙(抵制该要求:-),那么从技术上讲,可以将该更新或 proc 放在您的其余部分中,但是您会受到很大的并发打击,因为每个插入都等待前一个提交。

我不能相信这一点;我从Itzik那里学到了这一切。

于 2009-07-29T04:34:00.050 回答
5

使该字段成为身份字段。该字段将自动获取其值。您可以通过调用 SCOPE_IDENTITY() 或通过查询系统变量@@IDENTITY 来获取最后插入的值

SCOPE_IDENTITY() 函数是首选。

于 2009-03-19T12:25:17.970 回答
3

正如 DHeer 所说,绝对没有完全匹配。如果您尝试构建自己的过程来执行此操作,您总是会阻止您的应用程序扩展。

Oracle 的序列具有高度可扩展性。

好的,我稍微收回一下。如果您真的愿意专注于并发,并且愿意尽可能使用序列将数字弄乱,那么您就有机会。但是由于您一开始似乎对 t-sql 很不熟悉,所以我会开始寻找其他一些选项(将 Oracle 应用程序移植到 MSSS - 这就是您正在做的事情)

例如,只需在“nextval”函数中生成一个 GUID。这将扩大规模。

哦,不要为所有值使用一个表,只是为了将你的最大值保存在缓存中。您必须锁定它以确保您提供唯一值,这就是您停止缩放的地方。您必须弄清楚是否有办法在内存中缓存值并以编程方式访问某种轻量级锁——内存锁,而不是表锁。

于 2009-03-19T15:28:20.957 回答
1

我希望 SQL Server 有这个功能。它会让很多事情变得更容易。

这是我解决这个问题的方法。

创建一个名为 tblIdentities 的表。在此表中放置一行,其中包含您的最小值和最大值以及应重置序列号的频率。还要输入一个新表的名称(称为 tblMySeqNum)。这样做使得以后添加更多的序列号生成器变得相当容易。

tblMySeqNum 有两列。ID(它是一个 int 标识)和 InsertDate(它是一个默认值为 GetDate() 的日期时间列)。

当你需要一个新的 seq num 时,调用一个插入到这个表中的 sproc 并使用 SCOPE_IDENTITY() 来获取创建的身份。确保您没有超过 tblIdentities 中的最大值。如果你有然后返回一个错误。如果没有返回您的序列号。

现在,要重置和清理。有一项根据需要定期运行的作业,它检查 tblIdentites 中列出的所有表(目前只有一个)以查看它们是否需要重置。如果它们已达到重置值或时间,则在行中列出的表的名称上调用 DBCC IDENT RESEED(此示例中为 tblMySeqNum)。这也是清除该表中您并不真正需要的额外行的好时机。

不要在获得身份的存储过程中进行清理或重新播种。如果这样做,那么您的序列号生成器将根本无法很好地扩展。

正如我所说,SQL Server 中的这个特性会让很多事情变得更容易,但我发现这个工作在功能上相当好。

瓦卡诺

于 2009-03-20T16:39:25.287 回答
1

这可能很久以前就已经回答了......但从 SQL 2005 开始,您可以使用该ROW_NUMBER函数......一个例子是:

select ROW_NUMBER() OVER (ORDER BY productID) as DynamicRowNumber, xxxxxx,xxxxx

在我的情况下,该OVER语句使用ORDER BY唯一主键...

希望这会有所帮助......不再有临时表或奇怪的连接!

于 2009-05-17T14:26:04.270 回答
1

如果您能够更新到 SQL Server 2012,则可以使用 SEQUENCE 对象。甚至 SQL Server 2012 Express 也支持序列。

CREATE SEQUENCE supplier_seq
    AS DECIMAL(38)
    MINVALUE 1
    MAXVALUE 999999999999999999999999999
    START WITH 1
    INCREMENT BY 1
    CACHE 20;

SELECT NEXT VALUE FOR supplier_seq
SELECT NEXT VALUE FOR supplier_seq
SELECT NEXT VALUE FOR supplier_seq
SELECT NEXT VALUE FOR supplier_seq
SELECT NEXT VALUE FOR supplier_seq

结果是:

---------------------------------------
1

(1 row(s) affected)


---------------------------------------
2

(1 row(s) affected)


---------------------------------------
3

(1 row(s) affected)


---------------------------------------
4

(1 row(s) affected)


---------------------------------------
5

(1 row(s) affected)

只需注意指定正确的数据类型。如果我没有指定它,您提供的 MAXVALUE 将不会被接受,这就是我使用 DECIMAL 以尽可能高的精度的原因。

更多关于序列的信息:http: //msdn.microsoft.com/en-us/library/ff878091.aspx

于 2013-03-25T03:11:39.610 回答
0

不是一个真正的答案,但看起来序列将在 2012 年出现在 SQLServer 中。

http://www.sql-server-performance.com/2011/sequence-sql-server-2011/

于 2012-02-10T14:08:50.457 回答
0

不是一个确切的答案,而是一些现有答案的补充

SCOPE_IDENTITY (Transact-SQL)

SCOPE_IDENTITY、IDENT_CURRENT 和@@IDENTITY 是相似的函数,因为它们返回插入到标识列中的值。

IDENT_CURRENT 不受范围和会话限制;它仅限于指定的表。IDENT_CURRENT 返回为任何会话和任何范围内的特定表生成的值。有关详细信息,请参阅 IDENT_CURRENT (Transact-SQL)。

这意味着两个不同的会话可以具有相同的标识值或序列号,因此要避免这种情况并获得所有会话使用的唯一编号IDENT_CURRENT

于 2012-04-26T12:35:51.980 回答
0

正是因为这个 IDENT_CURRENT 不受范围和会话限制;它仅限于指定的表。我们需要使用 SCOPE_IDENTITY() 因为范围身份会给我们会话中生成的唯一编号,唯一性由身份本身提供。

于 2012-11-29T12:43:21.707 回答