【问题标题】:Identity specification身份规范
【发布时间】:2019-05-15 07:54:50
【问题描述】:

我在 SQL Server 2016 中创建表时遇到标识规范问题。

  • 在 Id 列中,我将 Identity Increment 和 Identity Seed 设置为 1。
  • 接下来我将新记录添加到新表中。
  • 在 Id 列中显示 2 个值。为什么?为什么不是 1 个值?
  • 接下来删除第一条记录并添加新记录。在 Id 列中显示 3 个值。为什么?为什么不是 1 个值。
  • 接下来我使用命令“update nametable set id=1”并收到无法更新标识列 ID 的答案。为什么?

【问题讨论】:

  • 何必呢? ID 值没有意义,但不应重复使用。
  • 之所以每次都获得一个新值,为什么从不重复使用旧值以及为什么不能更改 ID,仅仅是因为之前已经使用了其他值。 ID 必须永远唯一标识特定行。否则可能会导致混乱,破坏外键关系,并且通常难以理解哪个记录是哪个记录。
  • 例如,假设您有一个“图像”表,记录 1 存储一个文件名和一个描述“猫的图片”。记录 2 是一个文件名和描述“狗的图片。您将它们显示在网页上,人们可以为他们喜欢的图像添加书签 - 从逻辑上讲,书签与唯一 ID 相关联。现在假设您删除了图像 1。它不见了,人们的书签不再起作用,但你只是给他们一个“对不起,运气不好”的信息。
  • ...但是如果您随后添加一条名为“蠕虫图片”的新记录...如果 ID 1 可以重复使用,那么所有收藏 ID 1 的人都会访问现场看到了他们没想到的东西,一头雾水。这当然是一个微不足道的例子,但如果表格是“客户”、“员工”或“银行账户”,希望您能看到更严重的影响

标签: sql sql-server identity


【解决方案1】:

这可能用一些代码更容易解​​释:

CREATE TABLE YourTable (ID int IDENTITY(1,1),
                        SomeCol varchar(5));

INSERT INTO dbo.YourTable (SomeCol)
VALUES('abc'); --Will get ID 1

INSERT INTO dbo.YourTable (SomeCol)
VALUES('def'),('ghi'); --Will get 2 and 3.

SELECT *
FROM dbo.YourTable;

DELETE FROM dbo.YourTable;

INSERT INTO dbo.YourTable (SomeCol)
VALUES('abc'); --Will get ID 4, because 1-3 have been used. Deleting doesn't let you reuse values.

SELECT *
FROM dbo.YourTable;

DELETE FROM dbo.YourTable;

DBCC CHECKIDENT ('dbo.YourTable', RESEED, 1);

INSERT INTO dbo.YourTable (SomeCol)
VALUES('abc'); --Will get ID 2, as you seeded back to 1; so the NEXT ID is used.

SELECT *
FROM dbo.YourTable; 

TRUNCATE TABLE dbo.YourTable;

INSERT INTO dbo.YourTable (SomeCol)
VALUES('abc'); --Will get ID 4, because 1-3 have been used.

SELECT *
FROM dbo.YourTable; --Will get ID 1, as the column was reseed with the TRUNCATE


DROP TABLE dbo.YourTable;

对于您关于重新播种的具体问题,您定义的种子之后的下一个值是使用。您定义的种子是您所说的最后一次使用的种子。这在文档Forcing the current identity value to a new value 中有介绍:

由于表已有行,插入的下一行将使用 11 as the value – 为列定义的新的当前标识值 加 1(这是列的增量值)。

定义没有现有行的表的唯一方法是 TRUNCATE 它,这是我稍后在上述批次中所做的(以及为什么要重用 1)。

归根结底,您的IDENTITY 的值除了为该行提供单个使用值(不能保证它自己是唯一的)之外毫无意义。结合主键/唯一约束,它是一个很好的聚集索引候选,因为下一个值总是大于上次使用的值,并且值不会被重用。

如果具有连续值很重要,那么您需要使用SEQUENCE,而不是IDENTITY 属性。后者不保证唯一性或它自己的顺序值(因为它们可能由于删除、插入失败、意外关闭等而被跳过),但它确实保证一旦它们被重用(没有RESEED):IDENTITY (Transact-SQL) - RemarksSEQUENCE 可用于确保值确实是连续的(除了由于 DELETE)。

【讨论】:

  • 您还可以使用 DBCC CHECKIDENT 将标识重置为 0。下一个标识值将是 1。 (除非在自创建以来从未有任何记录的表上使用种子值为 0 的 DBCC CHECKIDENT,在这种情况下,下一个标识值将是 0...)
  • 但是我同意你的观点,IDENTITY 列的值应该是没有意义的,除了提供一个唯一的值。 :)
  • 我会复制并粘贴我的 cmets 而不是,已编辑,抱歉,
【解决方案2】:

欢迎来到论坛:)

如果您使用创建表

Id INT IDENTITY(1,1)

然后插入的第一条记录的 Id = 1,但是,如果插入语句失败或事务回滚,则使用的标识被标记为已使用(或丢失),并且下一个插入语句将从 Id = 2 开始。

查看有关此主题的 Microsoft 文档: https://docs.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property?view=sql-server-2017

【讨论】:

    【解决方案3】:

    删除插入的行时(顺便说一下,当这些插入在事务中回滚时也会发生),标识值不会自动重置。身份功能“记住”它的最后一个值。

    当从表中删除旧记录并将新记录插入表中时,标识值中的空白也不会被填充。

    这就是身份的运作方式。这是一种简单而安全的机制。

    如果您(偶尔!)想要重置标识值,可以查看DBCC CHECKIDENT。我个人倾向于这样使用它:

    DBCC CHECKIDENT (MyTable, RESEED, 0) WITH NO_INFOMSGS;
    DBCC CHECKIDENT (MyTable, RESEED) WITH NO_INFOMSGS;
    

    (我按此顺序执行这两行。)

    不过,我建议不要在生产环境中使用这种做法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-22
      • 2018-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多