SQL Server保证标识列的顺序插入吗?

15
换句话说,以下“游标”方法是否保证可行:
1.从数据库中检索行。 2.保存返回记录中最大的ID以备后用,例如在 LastMax 中。 3.稍后执行查询"SELECT * FROM MyTable WHERE Id > {0}", LastMax 为了使其有效,我必须确保在步骤1中没有获取到的每一行都有一个大于LastMax的ID。 这是有保证的吗?还是可能会遇到奇怪的竞态条件?
5个回答

23

这里的“保证”指的是绝对不可能出现值比当前最大值小或相等的情况吗?不是,没有这样的保证。不过,会发生这种情况的情况有限:

  1. 某人禁用了标识插入并插入了一个值。
  2. 某人重新设置了标识列。
  3. 某人更改了增量值的符号(即将+1更改为-1)。

假设没有出现这些情况,您就可以安全地避免竞争条件导致下一个值低于现有值的情况。尽管如此,不能保证行将按其标识值的顺序进行 提交。例如:

  1. 打开一个事务,在带有标识列的表中插入数据。假设它获取了值42。
  2. 在同一表中插入另一个值并提交。假设它获取值43。

在第一个事务提交之前,43存在但42不存在。标识列仅仅是保留一个值,而不是指定提交的顺序。


4
+1 第二部分正是我的问题 -- 因此你的答案是否定的,因为如果我在 43 被提交和 42 被提交之间进行第一次检索,我将永远不会知道第 42 行。 - balpha
@balpha - 正确。身份列仅保留自动递增的值,对于值的顺序或提交的顺序没有任何保证。可能情况是,这些值按照它们的身份值的顺序提交,但这与它们被保证以该顺序存在是非常不同的。 - Thomas
+1 交易的可能性是完全正确的,我只是没有考虑到另外两个交易,第二个交易被先提交。 我正在删除我的答案。好消息是我终于获得了自己的纪律徽章!耶! - Patrick Karcher

5
我认为这可能会根据交易持续时间而出现问题。考虑以下事件序列:
  1. 事务A开始
  2. 事务A执行插入操作-这将在标识列中创建一个新条目
  3. 事务B开始
  4. 事务B执行插入操作-这将在标识列中创建一个新条目
  5. 事务B提交
  6. 您的代码执行其选择并查看第二个事务的标识值
  7. 事务A提交-
事务A插入的行将永远不会被您的代码找到。当执行步骤6时,它尚未提交。当执行下一个查询时,它也不会被找到,因为它在标识列中具有比查询所需值更低的值。
如果您使用read-uncommitted隔离模式执行查询,则可以正常工作。

+1 交易的可能性是绝对正确的,我只是没有考虑到两个其他交易,第二个交易先被提交。我正在删除我的答案。 - Patrick Karcher
第六步 - 您的代码执行其选择操作,并从第二个事务中获取标识值。在表上存在一个打开的事务时,这个SELECT代码将如何运行(假设您正在执行select * from tablename)? - variable
第6步 - 您的代码执行其选择并从第2个事务中看到标识值 - 当表上有一个打开的事务时,这个SELECT代码会如何运行(假设您正在执行select * from tablename)? - undefined

2

身份标识始终遵循定义身份的增量:

IDENTITY [(seed, increment)] http://msdn.microsoft.com/en-us/library/aa933196(SQL.80).aspx

它可以是正数或负数(您可以将其递增或递减)。如果您将身份标识设置为递增,则身份标识值始终大于先前的值,但如果回滚INSERT,则可能会丢失一些值。

是的,如果您将身份标识增量设置为正值,则循环逻辑将起作用。


1
唯一可能无法获取的记录是当某人打开标识插入并手动插入一个跳过的 ID 记录(或在某些情况下插入负数)。这是一个相当罕见的情况,通常只会由系统管理员执行。例如,可能会用于重新插入意外删除的记录。

0

SQL Server 保证的唯一事情就是您的 IDENTITY 列将始终递增。

但需要考虑以下几点:

  1. 如果发生插入失败,IDENTITY 列仍会递增;
  2. 如果发生回滚,IDENTITY 列不会返回到其先前的值;

这就解释了为什么 SQL Server 不保证连续的 INDENTITY。

使用 DBCC 命令可以重置 IDENTITY 列。但在执行此操作之前,请考虑以下几点:

  1. 确保没有其他表引用您的 IDENTITY 列,否则外键可能无法更新,从而导致大麻烦;
  2. 您可以使用 SET IDENTITY_INSERT ON/OFF 指令,在插入行时手动指定 IDENTITY(不要忘记在之后将其打开)。

IDENTITY 列是 DBRM 中永远不应更改的最重要元素之一。

这里有一个链接可以帮助你:理解IDENTITY列

编辑:你似乎做的事情是可行的,因为从LastMax开始的IDENTITY列将为每个插入的行递增。所以:

  1. 从数据表中选择行;
  2. 保存LastMax状态;
  3. 选择Id > LastMax的行。

3)只会选择IDENTITY列大于LastMax的行,因此是自从保存LastMax以来插入的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接