【问题标题】:How to add a column in TSQL after a specific column?如何在特定列之后添加 SQL 中的列?
【发布时间】:2011-12-17 12:09:08
【问题描述】:

我有一张桌子:

MyTable
    ID
    FieldA
    FieldB

我想更改表格并添加一列,使其看起来像:

MyTable
    ID
    NewField
    FieldA
    FieldB

在 MySQL 中我会这样:

ALTER TABLE MyTable ADD COLUMN NewField int NULL AFTER ID;

一行,漂亮,简单,效果很好。在微软的世界里我该如何做到这一点?

【问题讨论】:

标签: tsql alter-table


【解决方案1】:
  1. 新建添加新列表脚本例如:[DBName].[dbo].[TableName]_NEW
  2. COPY old table data to new table: INSERT INTO newTable ( col1,col2,...) SELECT col1,col2,... FROM oldTable
  3. 新旧检查记录相同:
  4. 删除旧表
  5. 将新表重命名为旧表
  6. 重新运行您的 sp 添加新的列值
-- 1. Create New Add new Column Table Script
CREATE TABLE newTable
(   [new_column] [int] NOT NULL, <-- new column has been inserted here!
    [idx] [bigint] NOT NULL,
    [name] [nvarchar](30) NOT NULL,
    [active] [bit] NOT NULL
)
-- 2. COPY old table data to new table:
INSERT INTO newTable ([new_column],[idx],[name],[active])
SELECT [new_column],[idx],[name],[active]
FROM oldTable
-- 3. Check records old and new are the same: 
select sum(cnt) FROM (
SELECT 'table_1' AS table_name, COUNT(*) cnt FROM newTable 
UNION
SELECT 'table_2' AS table_name, -COUNT(*)  cnt FROM oldTable
) AS cnt_sum 
-- 4. DROP old table
DROP TABLE oldTable
-- 5. rename newtable to oldtable
USE [DB_NAME]
EXEC sp_rename newTable, oldTable

【讨论】:

    【解决方案2】:

    如果您使用 GUI 执行此操作,则必须取消选择以下允许删除表的选项,

    【讨论】:

      【解决方案3】:

      即使问题很老,也需要有关 Management Studio 的更准确答案。

      您可以手动或使用 Management Studio 创建列。但是 Management Studio 将需要重新创建表,如果您已经有太多数据,则会导致超时,除非表很轻,否则请避免。

      要更改列的顺序,您只需在 Management Studio 中移动它们即可。这不应该要求(很可能存在异常)Management Studio 重新创建表,因为它很可能会更改表定义中列的排序。

      我曾多次使用这种方法处理表格,但由于表格中的数据,我无法使用 GUI 添加列。然后使用 Management Studio 的 GUI 移动列并简单地保存它们。

      您将从确定的超时变为等待几秒钟。

      【讨论】:

        【解决方案4】:

        解决方案:

        这适用于不依赖更改表的表,这会触发级联事件。 首先确保您可以删除要重组的表而不会产生任何灾难性的后果。记下与您的表相关的所有依赖项和列约束(即触发器、索引) , 等等。)。完成后您可能需要将它们放回原处。

        步骤 1: 创建临时表以保存要重组的表中的所有记录。不要忘记添加新列

        CREATE TABLE #tmp_myTable
        (   [new_column] [int] NOT NULL, <-- new column has been inserted here!
            [idx] [bigint] NOT NULL,
            [name] [nvarchar](30) NOT NULL,
            [active] [bit] NOT NULL
        )
        

        第 2 步:确保已复制所有记录并且列结构符合您的要求。

        SELECT TOP 10 * FROM #tmp_myTable ORDER BY 1 DESC -- 你可以做 COUNT(*) 或任何事情来确保你复制了所有的记录

        第 3 步: 删除原始表:

        DROP TABLE myTable

        如果您对可能发生的坏事感到偏执,只需重命名原始表(而不是删除它)。这样它总是可以被退回。

        EXEC sp_rename myTable, myTable_Copy
        

        第 4 步: 以您想要的方式重新创建表 myTable(应匹配 #tmp_myTable 表结构)

        CREATE TABLE myTable
        (   [new_column] [int] NOT NULL,
            [idx] [bigint] NOT NULL,
            [name] [nvarchar](30) NOT NULL,
            [active] [bit] NOT NULL
        )
        

        -- 不要忘记您可能需要的任何约束

        第 5 步: 将临时 #tmp_myTable 表中的所有记录复制到新的(改进的)表 myTable 中。

        INSERT INTO myTable ([new_column],[idx],[name],[active])
        SELECT [new_column],[idx],[name],[active]
        FROM #tmp_myTable
        

        第 6 步:检查是否所有数据都返回到您的新改进表 myTable 中。如果是,请自行清理并删除临时表 #tmp_myTablemyTable_Copy 表(如果您选择重命名而不是删除它)。

        【讨论】:

        • 这是 SSDT 如何处理它的基本要点。 1)使用新定义创建临时表。 2) 在临时表上创建聚集索引 3) 如果旧表中存在数据,INSERT INTO temp (columns...) SELECT (columns) FROM old 4) 删除旧表 5) 表上的 sp_rename 然后聚集索引 6) 重新创建所有其他索引
        【解决方案5】:

        在 SQL Enterprise Management Studio 中,打开您的表,将列添加到您想要的位置,然后生成更改脚本,而不是保存更改。您可以看到它是如何在 SQL 中完成的。

        总之,别人说的都是对的。 SQL Management Studio 将您的所有数据提取到临时表中,删除该表,使用正确顺序的列重新创建它,然后将临时表数据放回那里。在特定位置添加列没有简单的语法。

        【讨论】:

          【解决方案6】:

          这是绝对可能的。尽管除非您知道自己在处理什么,否则您不应该这样做。 我花了大约 2 天时间才弄明白。 这是我输入的存储过程: - -数据库名称 (为了便于阅读,模式名称为“_”) ---表名 - -柱子 ---列数据类型 (添加的列始终为空,否则将无法插入) ---新列的位置。

          由于我正在使用来自 SAM 工具包的表(其中一些具有 > 80 列),因此典型变量将无法包含查询。这迫使需要外部文件。现在请注意您存储该文件的位置以及谁有权访问 NTFS 和网络级别。

          干杯!

          USE [master]
          GO
          /****** Object:  StoredProcedure [SP_Set].[TrasferDataAtColumnLevel]    Script Date: 8/27/2014 2:59:30 PM ******/
          SET ANSI_NULLS ON
          GO
          SET QUOTED_IDENTIFIER ON
          GO
          CREATE PROCEDURE [SP_Set].[TrasferDataAtColumnLevel]
          (
              @database varchar(100),
              @table varchar(100),
              @column varchar(100),
              @position int,
              @datatype varchar(20)    
          )
          AS
          BEGIN
          set nocount on
          exec  ('
          declare  @oldC varchar(200), @oldCDataType varchar(200), @oldCLen int,@oldCPos int
          create table Test ( dummy int)
          declare @columns varchar(max) = ''''
          declare @columnVars varchar(max) = ''''
          declare @columnsDecl varchar(max) = ''''
          declare @printVars varchar(max) = ''''
          
          DECLARE MY_CURSOR CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR 
          select column_name, data_type, character_maximum_length, ORDINAL_POSITION  from ' + @database + '.INFORMATION_SCHEMA.COLUMNS where table_name = ''' + @table + '''
          OPEN MY_CURSOR FETCH NEXT FROM MY_CURSOR INTO @oldC, @oldCDataType, @oldCLen, @oldCPos WHILE @@FETCH_STATUS = 0 BEGIN
          
          if(@oldCPos = ' + @position + ')
          begin
              exec(''alter table Test add [' + @column + '] ' + @datatype + ' null'')
          end
          
          if(@oldCDataType != ''timestamp'')
          begin
          
              set @columns += @oldC + '' , '' 
              set @columnVars += ''@'' + @oldC + '' , ''
          
              if(@oldCLen is null)
              begin
                  if(@oldCDataType != ''uniqueidentifier'')
                  begin
                      set @printVars += '' print convert('' + @oldCDataType + '',@'' + @oldC + '')'' 
                      set @columnsDecl += ''@'' + @oldC + '' '' + @oldCDataType + '', '' 
                      exec(''alter table Test add ['' + @oldC + ''] '' + @oldCDataType + '' null'')
                  end
                  else
                  begin
                      set @printVars += '' print convert(varchar(50),@'' + @oldC + '')'' 
                      set @columnsDecl += ''@'' + @oldC + '' '' + @oldCDataType + '', '' 
                      exec(''alter table Test add ['' + @oldC + ''] '' + @oldCDataType + '' null'')
                  end
              end
              else
              begin 
                  if(@oldCLen < 0)
                  begin
                      set @oldCLen = 4000
                  end
                  set @printVars += '' print @'' + @oldC 
                  set @columnsDecl += ''@'' + @oldC + '' '' + @oldCDataType + ''('' + convert(character,@oldCLen) + '') , '' 
                  exec(''alter table Test add ['' + @oldC + ''] '' + @oldCDataType + ''('' + @oldCLen + '') null'')
              end
          end
          
          if exists (select column_name from INFORMATION_SCHEMA.COLUMNS where table_name = ''Test'' and column_name = ''dummy'')
          begin
              alter table Test drop column dummy
          end
          
          FETCH NEXT FROM MY_CURSOR INTO  @oldC, @oldCDataType, @oldCLen, @oldCPos END CLOSE MY_CURSOR DEALLOCATE MY_CURSOR
          
          set @columns = reverse(substring(reverse(@columns), charindex('','',reverse(@columns)) +1, len(@columns)))
          set @columnVars = reverse(substring(reverse(@columnVars), charindex('','',reverse(@columnVars)) +1, len(@columnVars)))
          set @columnsDecl = reverse(substring(reverse(@columnsDecl), charindex('','',reverse(@columnsDecl)) +1, len(@columnsDecl)))
          set @columns = replace(replace(REPLACE(@columns, ''       '', ''''), char(9) + char(9),'' ''), char(9), '''')
          set @columnVars = replace(replace(REPLACE(@columnVars, ''       '', ''''), char(9) + char(9),'' ''), char(9), '''')
          set @columnsDecl = replace(replace(REPLACE(@columnsDecl, ''  '', ''''), char(9) + char(9),'' ''),char(9), '''')
          set @printVars = REVERSE(substring(reverse(@printVars), charindex(''+'',reverse(@printVars))+1, len(@printVars))) 
          
          create table query (id int identity(1,1), string varchar(max))
          
          insert into query values  (''declare '' + @columnsDecl + ''
          DECLARE MY_CURSOR CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR '')
          
          insert into query values   (''select '' + @columns + '' from ' + @database + '._.' + @table + ''')
          
          insert into query values  (''OPEN MY_CURSOR FETCH NEXT FROM MY_CURSOR INTO '' + @columnVars + '' WHILE @@FETCH_STATUS = 0 BEGIN '')
          
          insert into query values   (@printVars )
          
          insert into query values   ( '' insert into Test ('')
          insert into query values   (@columns) 
          insert into query values   ( '') values ( '' + @columnVars + '')'')
          
          insert into query values  (''FETCH NEXT FROM MY_CURSOR INTO  '' + @columnVars + '' END CLOSE MY_CURSOR DEALLOCATE MY_CURSOR'')
          
          declare @path varchar(100) = ''C:\query.sql''
          declare @query varchar(500) = ''bcp "select string from query order by id" queryout '' + @path + '' -t, -c -S  '' + @@servername +  '' -T''
          
          exec master..xp_cmdshell @query
          
          set @query  = ''sqlcmd -S '' + @@servername + '' -i '' + @path
          
          EXEC xp_cmdshell  @query
          
          set @query = ''del ''  + @path
          
          exec xp_cmdshell @query
          
          drop table ' + @database + '._.' + @table + '
          
          select * into ' + @database + '._.' + @table + ' from Test 
          
          drop table query
          drop table Test  ')
          

          结束

          【讨论】:

          • 该过程可以改进的地方是您可以将默认值添加到新列,然后将旧列分成两部分:在新列之前和之后。插入值时,在插入的 select 语句中添加“@beforeColumn + default + @after column”。如果可用,请提出更多改进建议:)
          【解决方案7】:

          /* 更改表格列顺序的脚本
          请注意,这将创建一个新表来替换原始表
          但是它不会复制触发器或其他表属性 - 只是数据
          */

          按您需要的顺序生成包含列的新表

          Select Column2, Column1, Column3 Into NewTable from OldTable

          删除原表

          Drop Table OldTable;

          重命名新表

          EXEC sp_rename 'NewTable', 'OldTable';

          【讨论】:

            【解决方案8】:

            在 Microsoft SQL Server Management Studio(MSSQL 的管理工具)中,只需进入表格的“设计”并将列拖到新位置。不是命令行,但你可以做到。

            【讨论】:

              【解决方案9】:

              如果您使用 Management Studio 中的 GUI 创建列,您应该能够执行此操作。我相信管理工作室实际上是在完全重建表格,这就是为什么会发生这种情况。

              正如其他人所提到的,表中列的顺序无关紧要,如果确实如此,则说明您的代码有问题。

              【讨论】:

                【解决方案10】:

                很遗憾你不能。

                如果您真的希望它们按该顺序排列,则必须创建一个新表,其中的列按该顺序排列并复制数据。或重命名列等。没有简单的方法。

                【讨论】:

                • 您只需在 Management Studio 中拖动列。这很容易
                • Management Studio 可以为您销毁和重新创建表,但是如果您有一个非常非常大的表,其中包含很多行,那么最好只是更改它。如果有办法在不破坏桌子的情况下做到这一点,那就太好了。 Management Studio 实际上有一个选项可以阻止您这样做,因为它可能非常耗时。
                • @PreguntonCojoneroCabrón Management Studio 在需要对表进行破坏性更改时将数据复制到临时位置的意义上维护数据。在表中按特定顺序放置列就是这样一种操作,其中需要删除并重新创建表。 SSMS 复制数据,删除表,使用新架构重新创建表,然后将数据添加回表中。
                【解决方案11】:

                您必须重建表。幸运的是,列的顺序根本不重要!

                观看我神奇地重新排列您的列:

                SELECT ID, Newfield, FieldA, FieldB FROM MyTable
                

                这也被问过无数次了。

                【讨论】:

                • 好吧,如果我直接通过 SQL 访问它可能不会,但我不是。另外我希望我的所有表都设置相同(升级和新创建)。
                • 管理工作室中出现列顺序。为了约定,在非常大的桌子上顺序可能很重要。
                • 虽然不是问题的答案,但它仍然是我遇到的最有趣的答案
                • 列的顺序可能很重要,例如,如果您有数十亿个条目。那么创建一个alignment padding 可能会很有趣,它可能会为您节省一些千兆字节。
                猜你喜欢
                • 2018-05-01
                • 2013-07-06
                • 2020-03-08
                • 1970-01-01
                • 2013-03-07
                • 1970-01-01
                • 2021-08-15
                • 2014-01-25
                • 2022-10-17
                相关资源
                最近更新 更多