【问题标题】:How to alter a primary key in an SQL Server 2005 database without losing any data?如何在不丢失任何数据的情况下更改 SQL Server 2005 数据库中的主键?
【发布时间】:2009-05-14 04:44:52
【问题描述】:

我的数据库中有一个表,它有一个主键,它的数据类型是 varchar(10),但它只存储 5 个字符,所以我想将它的数据类型从 varchar(10) 更改为 varchar(5)丢失任何数据。

目前我正在创建一个临时表,将数据从原始表移动到临时表,然后对其进行更改,最后再次将数据从临时表恢复到主表。

我想知道这个问题是否有其他解决方案?

【问题讨论】:

  • 何必呢?在几乎所有的存储引擎中,varchar(5) 使用的存储空间并不比 varchar(10) 少。
  • @kquinn: 正好相反——我知道的每个 DBMS 使用的 VARCHAR(5) 空间都比 VARCHAR(10) 少。在我最了解的 DBMS 中,开销会从 9% 增加到 17%(因为有一个字节用于存储实际长度);我可能会使用 CHAR(5),因为浪费很少。
  • 同意,CHAR(5) 在这里听起来最好……但在 MySQL 和 PostgreSQL 中,VARCHAR(5) 和 VARCHAR(10) 将使用相同数量的存储空间,例如字符串 ' A B C D'。在 PG 中它是 5 个字节:一个长度字节(如果长度 > 126 则为 4)加上四个数据(我不 认为 字符串存储为空终止;文档没有说但建议它们不是) . IIRC MySQL 的方式相同,尽管它可能使用 2 字节长度的标头。在这两种情况下,VARCHAR(5) 和 VARCHAR(10) 将在磁盘上为相同的字符串使用相同的空间。
  • @kquinn:重新审视了这个问题 - 我知道你在做什么,是的,分配给 VARCHAR(10) 存储 5 个字节和 VARCHAR(5) 存储 5 个字节的实际磁盘通常是相同。空间分配通常可能不同(在这种情况下是边际的)。同样,我知道的一个系统会确保页面上有足够的空间容纳最大大小的行,如果没有足够的空间,它将使用不同的页面。这对于许多非常长的可变长度字段很重要(例如,当最大实际长度仅为 32 时,VARCHAR(255) 的 6 个字段)。

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


【解决方案1】:

在大多数 DBMS 中,运行一个简单的 ALTER TABLE 语句可以一次性完成整个工作,而不会丢失任何数据。你确定 MS SQL Server 不支持吗? (如果没有,也许您需要更好的 DBMS - 但我认为这不是问题。)

看到概述的多步骤操作,我很困惑。在我主要使用的 DBMS 中,所需要的只是:

ALTER TABLE WhatEver MODIFY pkcolumn VARCHAR(5) NOT NULL;

它甚至可以是“原地改变”;它似乎几乎立即完成,即使实际更改将在很长一段时间内发生,因为实际行已更改。 (好的;实际上对于可能不会出现的 VARCHAR 字段;对于 CHAR 字段 - 当长度仅为 5 或 10 个字节时,这将是一个不错的选择 - 这将是一个“就地更改”。)

【讨论】:

    【解决方案2】:

    对于 db2 数据库,我不得不这样做。这些是 DBA 建议的步骤:

    1. 删除依赖对象,例如视图或过程...请。在做drop之前保留一份ddl的副本
    2. 将现有表重命名为 _old
    3. 创建结构正确的表。
    4. 对 _old 表执行 runstats
    5. 将数据从 _old 表加载到这个新表中。如果要保留标识列,请使用标识覆盖标志加载
    6. 重新创建视图和过程
    7. 重新绑定所有依赖的过程。

    如果是 db2 特定的,您可以省略第 4 步和第 7 步。此外,如果 SQLServer 中没有问题,您可以省略删除和重新创建依赖对象。

    【讨论】:

      【解决方案3】:

      由于您指定使用的是 SQL Server,因此可以而且应该使用设计工具进行更改。

      启动 Management Studio 程序并连接到服务器。然后打开有问题的数据库并找到表。

      右键单击表格并选择设计,然后在该视图中进行更改。

      你现在有两个选择:

      1. 您可以单击“保存”按钮,MS SQL Server 将为您编写脚本并执行必要的更改
      2. 或者,您可以使用“生成更改脚本”按钮。在我的 IDE 中,此按钮是与键/约束按钮相同的工具栏上最左侧的按钮(与具有保存按钮的工具栏不同)

      如果您使用后一个功能,您将在对话框中看到脚本代码,并且可以选择将其保存到磁盘。这使您可以大致了解要执行它时会发生什么。

      更改主键的问题在于,从其他表到该主键的所有引用都需要先删除,然后重新创建,并且 SQL Server Management Studio 程序足够智能,可以生成(至少在 99 % 的案例)您需要的所有 SQL,以正确的顺序。

      【讨论】:

        【解决方案4】:

        我认为您应该创建一个具有标识 int 主键的表,并使用您的 varchar 字段作为数据库的唯一替代自然键。

        我知道这意味着您必须更改指向那里的任何其他外键,但如果您已经在做您描述的工作,我会全力以赴。

        恕我直言

        【讨论】:

        • -1:并非每个表都需要代理键。我不是 Celko——我确实认识到它们的优势并在适当的时候使用它们,这在很多时候都是如此——但是自然键有几个优势。盲目使用和推荐自动递增代理键是愚蠢的。
        • 我指示使用代理键来替换当前用作主键的键,以便在需要时保留它。
        【解决方案5】:

        好吧,我发现的最有价值的方法是使用以下表达式创建一个备份表:

        SELECT *
        INTO your_table_name_backup 
        FROM your_table_name_original
        

        这将自动生成名称为 your_table_name_backup 的表的副本

        您现在可以通过删除和创建来更改原始表 (your_table_name_original)。

        之后做

        INSERT INTO your_table_name_original SELECT * FROM your_table_name_backup
        

        这样就不会丢失数据,也不会插入新的表字段类型或约束:)

        注意事项:

        我之所以写这篇文章是因为没有人将 MSQLMS 称为在不删除和创建的情况下更改表的压力,例如,当您使用设计器更改字段类型时,您可以看到这是一条漂亮的消息。我不明白为什么他们让你打开设计器并实际编辑字段类型,如果最后你收到这样的消息是不允许的 yadayadayada...

        如果有人知道更简单的方法,请提及它,希望它可以帮助某人......

        【讨论】:

          【解决方案6】:

          总体思路如下。不要忘记先备份您的数据库。

          ALTER TABLE tableName ADD tempColumn VARCHAR(5)
          
          UPDATE tableName SET tempColumn = pkColumn
          
          -- Do this for all constraints
          
          ALTER TABLE tableName DROP CONSTRAINT pkConstraint
          
          ALTER TABLE tableName DROP COLUMN pkColumn
          
          ALTER TABLE tableName ADD pkColumn VARCHAR(5)
          
          UPDATE tableName SET pkColumn = tempColumn
          
          ALTER TABLE tableName DROP COLUMN tempColumn
          

          【讨论】:

          • 你确定它需要这么复杂吗?真的确定吗?
          • 不。通常不必那么困难。但是那个人在这里问..浪费我们宝贵的时间,而不是仅仅阅读文档。所以我不介意让他做一些额外的工作。也许这会促使他用自己的头脑寻找更简单的解决方案。
          猜你喜欢
          • 1970-01-01
          • 2015-07-03
          • 2010-09-10
          • 1970-01-01
          • 1970-01-01
          • 2019-09-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多