【问题标题】:Changing a table's primary key column referenced by foreign key in other tables更改其他表中外键引用的表的主键列
【发布时间】:2011-05-30 08:03:06
【问题描述】:

在我们的数据库中(在 SQL Server 2005 上)我们有一个“客户”表,其主键是客户端代码,一个代理项,bigint IDENTITY(1,1) 键;该表被我们数据库中的许多其他表通过外键引用。

我们估计的新 CR 实现需要我们将 ID 列类型更改为 varchar,客户端代码生成算法从简单的数字级数转换为严格的 2 字符表示,代码范围从 01 到 99,然后进行像这样:

1A -> 2A -> ... -> 9A -> 1B -> ... 9Z

我对数据库设计还很陌生,但我在这里发现了一些严重的问题。首先,这个客户端代码生成算法呢?如果我需要客户代码超过 9Z 代码限制怎么办?

我有一个问题:这个改变是否可行,表已经填充了相当数量的数据,并被多个实体引用?如果是这样,您将如何解决这个问题,以及如何实现客户端代码生成?

【问题讨论】:

  • 为什么不为 varchar ID 添加另一列并保持旧列不变??
  • 如果您有兴趣,我已经在我的答案中添加了更多信息。如果对您有好处,请不要忘记关闭答案;-)
  • 你是对的。新的 CR 实现是 JURASSIC。告诉那些恐龙爬进沥青坑然后灭绝。您不想在主键中嵌入任何有意义的数据;其唯一目的是唯一标识该行。一种折衷方案是添加一个带有唯一索引的新列,以存储时髦的恐龙遗留键。
  • @Tim 不幸的是,恐龙似乎还活着而且很好...... :-)

标签: sql sql-server database sql-server-2005 database-design


【解决方案1】:

我将保留主键,并在生成的客户端代码上创建另一个键(唯一)。 无论如何我都会这样做。使用短数字主键而不是长字符键总是更好。 在某些情况下,您可能更喜欢 GUID(用于复制目的),但数字 int/bigint 总是更可取。 你可以阅读更多herehere

【讨论】:

  • 我在大型系统中的 sql server 2005/2008 中有证据表明 7-8 个字符的 varchar 键实际上比 int 主键
  • 可能是。我倾向于选择独一无二且永远不会改变的PK。我个人使用 GUID (Comb)。
【解决方案2】:

最大担心您的提议是您将被限制为 360 条主要记录。这似乎是一个很小的数字。

执行更改是一个多步骤操作。您需要在核心表及其所有相关表中创建新字段。

要进行就地更新,您需要在核心表中生成代码。然后,您需要更新所有相关表以具有基于 old id 的代码。然后您需要将外键约束添加到所有相关表中。然后您需要从所有相关表中删除旧的关键字段。

我们只在我们的开发服务器中这样做了。当我们升级实时数据库时,我们为每个数据库创建了一个新数据库,并使用查询旧数据库并插入新数据库的 python 脚本复制数据。我现在为每次软件升级更新该脚本,以便核心引擎保持不变,但我可以指定不同的表或数据修改。如果在升级生产时发生意外情况,我可以获得原始数据库的完整备份。

支持非身份/guid 代码的一个强有力的论据是,您需要一个人类可读/可记忆的代码,并且您需要能够在两个系统之间移动记录。

在 SQL Server 2005 和 2008 中,性能不一定是一个问题。我们最近进行了一次更改,我们从无处不在的 int id 移动到 7 或 8 个字符的“友好”记录代码。我们预计会看到某种性能下降,但实际上我们看到了性能改进

我们还发现我们需要一种快速生成代码的方法。我们的代码有两部分,一个 3 个字符的字母前缀和一个 4 或 5 位数字的后缀。一旦我们有大量代码(15000-20000),我们发现将代码解析为前缀和后缀并找到最低的未使用代码会很慢(需要几秒钟)。正因为如此,我们分别存储前缀和后缀(在主键表中),以便我们可以快速找到具有特定前缀的下一个可用的最低代码。缓存的前缀和后缀使搜索几乎收费。

我们允许更改代码,并且它们更改的值通过外键关系上的级联更新规则传播。我们在核心代码表上保留了一个身份密钥,以简化代码的更新。

我们使用 ORM,所以我不知道需要注意哪些具体事项。在我们最大的实例中,我们也有大约 60,000 个主键,但有数百个与代码表相关的表和数百万个相关值的表。

我们获得的一大优势是,在许多情况下,我们不需要执行连接即可执行操作。在软件的任何地方,用户都通过友好的代码引用事物。我们不必查找 int ID(或连接)来执行某些操作。

【讨论】:

    【解决方案3】:

    新的代码生成算法不值得考虑。您可以编写一个程序,只需几行代码即可生成所有可能的代码。把它们放在一张桌子上,你就完成了。您只需要编写一个函数来返回尚未使用的最小函数。这是一个 Ruby 程序,它将为您提供所有可能的代码。

    # test.rb -- generate a peculiar sequence of two-character codes.
    i = 1
    ('A'..'Z').each do |c|
      (1..9).each do |n|
        printf("'%d%s', %d\n", n, c, i)
        i += 1
      end
    end
    

    该程序将创建一个 CSV 文件,您应该能够轻松地将其导入到表格中。您需要两列来控制排序顺序。新值自然不会按照您的要求指定的方式排序。

    比起算法,我更关心范围。如果您的要求是正确的,那么您将被限制为 234 个客户端代码。如果你错了,并且范围从“1A”到“ZZ”,那么你被限制在不到一千。

    要在现有表中实现此要求,您需要遵循谨慎的程序。在生产表上尝试之前,我会在测试环境中尝试几次。 (这只是一个草图。有很多细节。)

    • 创建并填充包含两列的表以进行映射 新 CHAR(2) 的现有 bigint。
    • 在所有 需要它们的表。
    • 更新所有新的 CHAR(2) 列。
    • 在新的 CHAR(2) 列上创建新的 NOT NULL UNIQUEPRIMARY KEY 约束和新的 FOREIGN KEY 约束。
    • 重写用户界面代码 (?) 以定位新列。 (可能如果您重命名新的 CHAR(2) 和旧的 BIGINT 列。)
    • 设置目标日期以删除旧的 BIGINT 列和约束。
    • 等等。

    【讨论】:

      【解决方案4】:

      没有真正解决这是否是一个好主意,但您可以更改外键以级联更新。完成后会发生什么,当您更新父表中的主键时,子表中的相应键将相应更新。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-26
        • 2014-05-05
        • 1970-01-01
        • 1970-01-01
        • 2014-10-15
        • 1970-01-01
        • 2012-07-09
        • 1970-01-01
        相关资源
        最近更新 更多