【问题标题】:Is Access's AutoNumber (Increment) guaranteed to increment?Access 的自动编号(增量)是否保证增加?
【发布时间】:2010-10-20 11:28:24
【问题描述】:

对于特定表,我将 ID 字段设置为自动编号(增量)。如果我快速连续向该表添加 5 行,是否保证每行的 ID 都比上一个大?例如,如果一些较早的值已被删除,自动编号是否会从 1 重新开始?

【问题讨论】:

    标签: ms-access auto-increment autonumber


    【解决方案1】:

    我唯一遇到过 Access 自动编号问题的情况是,我错误地将自动编号键字段的值设置为低于使用附加查询的当前最大值的数字。记录删除在编号中造成了空白。 Access 允许您将一个值强制输入自动编号字段,有时(并非总是如此,我不知道为什么)自动编号​​会重置为较小的数字。最终,随着记录的添加,我遇到了“重复键”错误。除此之外,我多年来从未遇到过麻烦。

    这个问题有几个关于增量值的答案。据我所知,Access自动编号字段只能设置为“增量”(按1)或“随机”,除了1之外没有其他方法可以设置数字增量。如果我对此有误,请指教我。

    【讨论】:

    • 我不知道通过 Access UI 指定增量的任何选项。但是,SQL DDL 为您提供了选项。 onedaywhen 演示了一个 CREATE TABLE。你也可以做一个 ALTER TABLE。无论哪种方式,您都可以指定种子值和增量值。
    • 我的立场是正确的。 onedaywhen 提供的代码在 Access 中不起作用,但我在试验后发现“CREATE TABLE testit (id AUTOINCREMENT(0,5), teststr CHAR)”确实有效。你学到的东西!
    【解决方案2】:

    遗憾的是,即使是 Microsoft 的应用程序也不是绝对可靠的。但这就是它的工作方式,我还没有看到它失败。也没有听说过它会因故意或意外颠覆而失败。

    不过,不要指望它对每个整数值都有一行。除了删除的行之外,它还会用完失败的追加操作的数字。

    【讨论】:

      【解决方案3】:

      “如果删除某些记录并且压缩数据库,则下一个标识将重置为使用的最低数字 + 1”的语句不正确。 Access 97 中使用的 Jet 3.5 中发生了这种情况,而 Access 2000 中使用的 Jet 4.0 中则没有。

      【讨论】:

      • 我不知道,托尼。感谢您纠正这一点。我想我不知道,因为我从不在乎 Autonumber 的值是什么。
      • @David:那你为什么喜欢递增而不是随机?
      • 我更喜欢递增,因为在系统上工作和查看表格时,1423 比 -874036470 更容易记住。
      • 随机自动编号在批量插入中也较慢。
      • @tony:David 是正确的,你犯了一个经典缺陷:赋予不应该向用户公开的值以意义,是的,包括高级用户和开发人员;-) 你确实应该“从不关心自动编号值是什么”。
      【解决方案4】:

      David W. Fenton 写道:“Jet Autonumber 字段不是身份字段。它只是一个具有特殊默认值的长整数字段。该默认值可以是 INCREMENT 或 RANDOM,但因为它只是一个默认值,您可以将任何长整数值附加到该字段,只要它不违反索引。”

      这有点混乱。 ACE/Jet SQL 语法有一个 IDENTITY 关键字(COUNTERAUTOINCREMENT)但没有 AUTONUMBER 关键字。显然 ACE/Jet IDENTITYIDENTITY

      但我想在这里解决的问题(评论太长)是关于它“只是一个具有特殊默认值的长整数字段”的错误陈述。

      考虑一下这个 ACE/Jet SQL DDL(ANSI-92 查询模式语法):

      CREATE TABLE Test2_IDENTITY
      (
         ID_identity_1 IDENTITY NOT NULL, 
         ID_identity_2 IDENTITY NOT NULL, 
         data_column INTEGER
      );
      

      执行时,它会失败并显示消息“结果表不允许有多个自动编号字段”。很明显,这里发生的不仅仅是“特殊默认值”。

      IDENTITY 关键字是使用递增算法创建一个自动编号(因为需要更好的术语)来生成值。 IDENTITY 不能用于使用随机算法或自动编号的 GUID(复制 ID)风格创建自动编号。对于这些其他情况,您确实需要使用“特殊默认值”,例如

      CREATE TABLE TestAutonumbers 
      (
         ID_identity IDENTITY NOT NULL, 
         ID_random INTEGER DEFAULT GenUniqueID() NOT NULL, 
         ID_guid UNIQUEIDENTIFIER DEFAULT GenGUID() NOT NULL, 
         data_col INTEGER
      );
      

      如果您使用 ADOX 等技术检查此表的属性(信息架构),您会发现只有使用 IDENTITY 关键字创建的列的 Autoincrement 属性设置为 true,并且该列的 COLUMN_HASDFAULT 为 false,COLUMN_DEFAULT 为空值。所以如果一个 IDENTITY 列确实有一个“特殊的默认值”,那么引擎不会告诉你。

      IDENTITY 不同,对于这些其他类型的自动编号,每个表没有明确的限制,例如这工作正常:

      CREATE TABLE Test2_Autonumbers
      (
         ID_random_1 INTEGER DEFAULT GenUniqueID() NOT NULL, 
         ID_random_2 INTEGER DEFAULT GenUniqueID() NOT NULL, 
         ID_guid_1 UNIQUEIDENTIFIER DEFAULT GenGUID() NOT NULL, 
         ID_guid_2 UNIQUEIDENTIFIER DEFAULT GenGUID() NOT NULL, 
         data_col INTEGER
      );
      

      我不知道是否存在等效于GenUniqueID()GenGUID() 的“特殊默认值”来使用DEFAULT 和不使用IDENTITY 关键字(或其同义词)创建自动增量列)。如果有人知道其中一种方法,请告诉我。

      顺便说一句,上述错误消息表明我将“自动编号”作为访问术语是错误的。似乎在 ACE/Jet 引擎级别“自动编号”是 IDENTITY 的非关键字同义词(即自动增量风格自动编号),但不是其他自动编号风格的同义词。

      【讨论】:

      • 您确实意识到,当您使用 ANSI 92 SQL 时,您不再使用 Jet SQL?实际的 Jet SQL 根本没有 IDENTITY 关键字。其次,Jet 文档是围绕您将使用 DAO 而不是 Jet DDL 的假设构建的,因为 DAO 完全支持表和字段的所有可能的 Jet 属性,而 Jet DDL 不支持。在我看来,您的观点似乎不是 Jet 的本地用户,而是习惯于使用其他数据库引擎的人。
      • 当使用 ADOX 读取 Jet 的结构时,您将返回 ADOX 语法,而不是原生 Jet 语法。 Jet Autonumbers 的行为方式与其他数据库上的标准 IDENTITY 字段类似。但这并不意味着它们的实现方式或行为方式相同 - 这只意味着对于某些用途,这是一种兼容的方式来对待它们。
      • 总之,如果您使用非本地接口来查看 Jet 数据库,您将无法获得本地术语,并且您可能会得到 Jet 对特定对象的实现的不完美表示。如果您想了解 Jet 的功能,请使用本机 Jet 接口,即 Jet 的 ANSI 89 DDL 和 DAO 方言。其他任何事情都将通过专门设计的翻译层提供给您,该翻译层旨在使所有数据库引擎在可能的范围内看起来都一样。
      • 您似乎依赖于 ANSI-89 查询模式语法是一种真正的 ACE/Jet 语法,而 ANSI-92 查询模式语法根本不是 ACE/Jet 语法。那会是什么呢?它不是 SQL Server 语法。它不适用于 Jet/ACE 以外的任何引擎。
      • @himself:很有趣。我无法让它工作,例如CREATE TABLE TestAuto (ID COUNTER UNIQUEIDENTIFIER NOT NULL, data_col INTEGER NOT NULL); 执行正常,但我最终得到一个递增列(如果你愿意的话,一个 IDENTITY 列);换句话说,UNIQUEIDENTIFIER 关键字被忽略。您可以在不使用DEFAULT 的情况下发布自动生成随机/guid 值的代码吗?很高兴看到。
      【解决方案5】:

      我有一个以前在复制数据库中使用的表。有些自动编号是负数,有些非常大。这对于复制表来说是正常的,因此您无法确定自动编号是否会大于前一个数字,它可能是负数。

      【讨论】:

      • 这是因为当复制 Jet 数据库时,自动编号会从 INCREMENT 转换为 RANDOM。这是必需的,否则,您最终会在不同副本之间立即发生冲突。
      • 也就是说,如果您的自动编号使用 INCREMENT 作为下一个值并且您还没有达到最大正值,那么下一个值将会更大。
      【解决方案6】:

      Jet Autonumber 字段不是身份字段。它只是一个具有特殊默认值的长整数字段。该默认值可以是 INCREMENT 或 RANDOM,但由于它只是一个默认值,因此您可以将任何长整数值附加到该字段,只要它不违反索引即可。

      递增的自动编号永远不会恢复为 1,除非您已删除所有记录并进行了压缩,或者在种子值损坏的情况下。后者在 Jet 4 的早期版本(Service Pack 6 之前)中经常发生,其中种子值会被重置,这会导致各种问题,包括损坏的 PK 索引。幸运的是,这个问题最终得到了解决,而且因为 Jet 是一个 Windows 组件,所以几乎没有任何计算机会安装 Jet 4 service pack 8。

      正如 onedaywhen 所说,如果递增的自动编号超过长整数的最大正值,您可以获得负值,但这向我表明您的表中可能有足够的记录,您需要不同的数据库引擎,或者您'不正确地将您的表视为临时表(即追加和删除大量记录)。

      正如其他人所说,唯一性不是由自动编号数据类型控制,而是由索引控制。如果您创建了非唯一索引,则可以附加重复值。我无法想象你为什么想要一个具有重复值的自动编号字段,但关键是如果你不添加唯一索引,你可以做到这一点。由于大部分 Autonumber 字段都用作代理主键,因此它们将具有唯一的 PK 索引,并且数据表将按 PK 顺序(聚集)写入。关于唯一性,如果您使用自动编号作为代理 PK,并且表中有任何应该唯一的自然键(并且 可以 是唯一的,即不允许 Nulls),您应该在自然键字段(单个字段或复合索引)上也有唯一索引。

      【讨论】:

      • 为什么要在自动编号上进行集群?不能优化查询,所以我猜你必须使用 RANDOM 来支持并发,即你是一个已知的 DAO 用户,DAO 只支持页面级锁定。
      • “Jet Autonumber 字段不是身份字段”我认为语义对您很重要?! “自动编号”是一个访问术语。 ACE/Jet 关键字是 COUNTER、AUTOINCREMENT 和 IDENTITY。因此,ACE/Jet IDENTITY 是一个具有增量算法的访问自动编号。
      • “递增的自动编号永远不会恢复为 1,除非您已删除所有记录并压缩,或者在种子值损坏的情况下”——还有一种情况:它可以显式使用 SQL DDL(或类似 ADOX 的组件)重新播种,例如ALTER TABLE MyTable ALTER ID INTEGER(500, 5) NOT NULL; (这是为什么所有拥有管理员权限的用户都可能成为问题的示例!!)
      • Jet 数据表聚集在 PK 上,因此如果您有一个自动编号作为 PK,它将聚集在该字段上。不知道你对随机和并发的意思。如果编辑主要集中在最近添加的记录上,我只建议在副本之外使用随机自动编号,这意味着随机自动编号 PK 会将最新记录分散到许多数据页上,这可以提高并发性。但我没有提出这样的建议。
      • 也许我不理解“身份字段”的定义,但我从 SQL 规范中的理解是,身份字段不能被写入,无论是使用 SQL UPDATE 还是 SQL INSERT。虽然在各种不满足该要求的各种数据库引擎中可能有许多称为“身份字段”的实现,但我认为使用 Jet Autonumber 进行区分很重要。
      【解决方案7】:

      首先,您的IDENTITY(访问中的自动编号)列必须是INTEGER(访问中的长整数)而不是UNIQUEIDENTIFIER(访问中的复制ID),必须使用增量算法而不是随机算法用于生成新值并假设您的种子和增量值分别为默认值 1,那么如果您删除表中的所有行并压缩数据库,则应将 IDENITTY 列重新设置为 1。如果不是那么您可能需要安装 Jet 服务包 (http://support.microsoft.com/kb/287756)。

      请注意,当INTEGER(访问中的长整数)的最大正值被下一个自动生成的值超过时,它将“包裹”到负的INTEGER 范围内,并将继续循环遍历正负范围,在必要时生成重复值(除非该列另外被唯一约束覆盖)。实际上,如果增量值足够大,您可以保证这些值将在大于和小于先前自动生成的值之间交替,例如(ACE/Jet ANSI-92 查询模式语法):

      CREATE TABLE Test 
      (
         ID INTEGER IDENTITY (0, 2147483647), 
         data_col INTEGER NOT NULL UNIQUE
      )
      ;
      INSERT INTO Test (data_col) VALUES (1)
      ;
      INSERT INTO Test (data_col) VALUES (2)
      ;
      INSERT INTO Test (data_col) VALUES (3)
      ;
      INSERT INTO Test (data_col) VALUES (4)
      ;
      INSERT INTO Test (data_col) VALUES (5)
      ;
      INSERT INTO Test (data_col) VALUES (6)
      ;
      INSERT INTO Test (data_col) VALUES (7)
      ;
      INSERT INTO Test (data_col) VALUES (8)
      ;
      

      自动生成的值为 0、2147483647、-2、2147483645、-4、2147483643、-6、2147483641 等。

      【讨论】:

        【解决方案8】:

        我的答案是 3 串 VBA 代码!

        Set db = CurrentDb
        Set tbl = db.TableDefs("your_tbl")
        Set current_index = tbl.Indexes.Item("PrimaryKey").DistinctCount
        

        【讨论】:

        • 这个答案解决了问题。我以这种方式抓住了这个问题:“如何在不执行 INSERT 查询的情况下获取下一条插入记录的 ID?即使在表被清除之后”。所以这就是答案。
        • DistinctCount 报告索引中不同键值的数量。所以一个有 2 行的表,自动编号主键 1 和 3,DistinctCount 会给你 2。这并不能说明下一个自动编号值是什么。
        • @HansUp 请不要忘记您使用 VBA(对象语言)而不是 SQL。所以它可以返回所有可能的东西。我检查了你的例子。所以我百分百确定。即使它被删除,它也会返回最后一个主键的值。对我来说,增量是将 1 添加到最后一个索引。
        • 我已经用一个有很多过渡的旧文件对此进行了测试。这段代码,一旦更正它运行,给我 4325,记录数,而下一个自动编号是 2135918951。我类似地测试了一个新的小文件,有几个删除和切换到文本索引。它告诉我当前索引是 1,这甚至不是记录数。刷新,通过切换到数字索引,它告诉我它是 2,下一个自动编号是 4。这是 msdn.microsoft.com/en-us/library/ff191836.aspx 所期望的,我怀疑 @hansup 知道他在比 VBA 更多的领域谈论什么。跨度>
        • Distinct count是匹配记录只计算一次的记录计数,与下一次自动编号无关。自己玩,你会看到的。
        【解决方案9】:

        如果删除了一些记录并且压缩了数据库,则下一个标识将重置为使用的最低数字 + 1 - 如果表被清空,则在压缩后下一个标识将设置回 1。

        【讨论】:

          猜你喜欢
          • 2018-05-07
          • 2012-04-02
          • 1970-01-01
          • 2021-02-15
          • 1970-01-01
          • 1970-01-01
          • 2011-12-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多