如何使用脚本将列从一种数据类型更改为另一种

时间:2016-08-23 09:43:48

标签: sql-server tsql

我必须更改大量表格30+一列的数据类型从fgetsInt。    我的问题是,特定列也适用于BigInt的一个表,而其他许多列PK。因此,当我收到错误消息时,我无法更改。

我需要一个算法或一些脚本。但我无法弄清楚步骤或方法:我应该使用光标,还是只使用临时表来删除记录并始终获取第一个...

我该怎么办?从堆栈中取出第一个表,检查所讨论的列是由PK还是FK引用,如果是这样,删除引用(PK,FK等)然后改变列类型,然后重新创建Pk和FK?我应该存储我掺杂的东西吗?我很困惑......

任何提示?

2 个答案:

答案 0 :(得分:3)

如果您有很多内容,一种选择是不删除记录,而是考虑为每个BigInt数据类型的表添加另一列,然后将数据从主键列移动到此新列。

ALTER TABLE Table1 ADD COLUMN NewPKColumn BigInt;
UPDATE Table1 SET NewPkColumn = CurrentPKColumn;

您对引用此新BigInt列的其他表执行相同操作,添加数据类型BigInt的新列,移动数据。

ALTER TABLE Table2 ADD COLUMN NewPKColumn_FK BigInt;
UPDATE Table2 SET NewPKColumn_FK = Current_FK_Column;
(repeat this for all tables...)

当您为所有表执行此操作后,您可以从原始PK列中删除PK并删除该列(但在此之前,请务必查看并查看创建的索引类型在PK栏上 - 群集或非群集)并且不要忘记再次创建FK。

ALTER TABLE Table2 DROP CONSTRAINT FK_Tbl1;
ALTER TABLE Table1 DROP COLUMN CurrentPKColumn;
ALTER TABLE Table2 ADD CONSTRAINT FK_Tbl1 REFERENCES Table1(NewPKColumn);

此外,您需要查看存在哪些索引并包含当前的PK列,因为您还需要重新创建这些索引。

而且,与往常一样,首先在开发环境中对此进行测试,以确保您没有跳过任何内容。

答案 1 :(得分:2)

好的,这将是一个脚本的野兽,但我现在正在解决同样的问题。此脚本仍在进行中,但应该可以帮助您完成您需要做的事情。

我当前的代码使用给定的表格和字段,随时可以编辑;

DECLARE @TableToEdit varchar(255); SET @TableToEdit = 'Entity'
DECLARE @MasterField sysname; SET @MasterField = 'Entity_Identifier'

您需要先删除所有外键;

IF OBJECT_ID('tempdb..#ForeignKeys') IS NOT NULL
    DROP TABLE #ForeignKeys

CREATE TABLE #ForeignKeys
(
    PKTABLE_QUALIFIER nvarchar(255) NULL
    ,PKTABLE_OWNER nvarchar(255) NULL
    ,PKTABLE_NAME nvarchar(255) NULL
    ,PKCOLUMN_NAME varchar(255) NULL
    ,FKTABLE_QUALIFIER nvarchar(255) NULL
    ,FKTABLE_OWNER nvarchar(255) NULL
    ,FKTABLE_NAME nvarchar(255) NULL
    ,FKCOLUMN_NAME nvarchar(255) NULL
    ,KEY_SQL int NULL
    ,UPDATE_RULE int NULL
    ,DELETE_RULE int NULL
    ,FK_NAME nvarchar(255) NULL
    ,PK_NAME nvarchar(255) NULL
    ,DEFERRABILITY int NULL
)


/* find all tables that have an Entity_Identifier field that's not already an int */
IF OBJECT_ID('tempdb..#MasterTables') IS NOT NULL DROP TABLE #MasterTables
CREATE TABLE #MasterTables (MasterTableName sysname, MasterTableSQL nvarchar(max))
INSERT INTO #MasterTables (MasterTableName, MasterTableSQL)
SELECT DISTINCT
o.name TableName
,'INSERT INTO #ForeignKeys EXEC sp_fkeys ' + o.name MasterTableSQL
FROM sys.objects o
JOIN sys.columns c
    ON o.object_id = c.object_id
JOIN sys.types t
    ON c.user_type_id = t.user_type_id
WHERE o.type = 'u'
--AND t.name NOT IN ('int','bigint')
--AND c.name LIKE '%Entity_Identifier%'
AND c.name LIKE '%' + @MasterField + '%'



/* Let's find all Foreign Keys based upon a table that has an Entity_Identifier field that needs to be converted */

DECLARE @execspfkeys nvarchar(max)

DECLARE spfkeyscursor CURSOR LOCAL FOR
    SELECT MasterTableSQL FROM #MasterTables 

OPEN spfkeyscursor

FETCH NEXT FROM spfkeyscursor INTO @execspfkeys

WHILE @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    EXEC sp_executesql @execspfkeys

    FETCH NEXT FROM spfkeyscursor INTO @execspfkeys
END

CLOSE spfkeyscursor
DEALLOCATE spfkeyscursor



/* Ok, let's get the foreign key definitions from all relevant tables */

IF OBJECT_ID('tempdb..#FKScripts') IS NOT NULL
    DROP TABLE #FKScripts
CREATE TABLE #FKScripts
(
    FKName nvarchar(255)
    ,FKTableName nvarchar(255)
    ,FKSchema nvarchar(10)
    ,FKDatabase nvarchar(255)
    ,PKName nvarchar(255)
    ,PKTableName nvarchar(255)
    ,PKSchema nvarchar(10)
    ,PKDatabase nvarchar(255)
    ,FKDisableScript nvarchar(max)
    ,FKRebuildScript nvarchar(max)
    ,FKCheckScript nvarchar(max)
)

INSERT INTO #FKScripts (FKName, FKTableName, FKSchema, FKDatabase, PKName, PKTableName, PKSchema, PKDatabase, FKDisableScript, FKRebuildScript, FKCheckScript)
SELECT DISTINCT 
    fk.FK_NAME
    ,fk.FKTABLE_NAME
    ,fk.FKTABLE_OWNER
    ,fk.FKTABLE_QUALIFIER
    ,fk.PK_NAME
    ,fk.PKTABLE_NAME
    ,fk.PKTABLE_OWNER
    ,fk.PKTABLE_QUALIFIER
    ,'ALTER TABLE [' + fk.FKTABLE_OWNER + '].[' + fk.FKTABLE_NAME + '] DROP CONSTRAINT [' + fk.FK_NAME + ']' FKDisableScript
    ,'ALTER TABLE [' + fk.FKTABLE_OWNER + '].[' + fk.FKTABLE_NAME + '] WITH CHECK ADD CONSTRAINT [' + fk.FK_NAME + '] FOREIGN KEY (' + PKList.FieldList +') REFERENCES [dbo].[' + fk.PKTABLE_NAME + '] (' + FKlist.FieldList + ')' FKRebuildScript
    ,'ALTER TABLE [' + fk.FKTABLE_OWNER + '].[' + fk.FKTABLE_NAME + '] CHECK CONSTRAINT [' + fk.FK_NAME + ']' FKCheckScript
FROM #ForeignKeys fk
INNER JOIN
    (
    SELECT DISTINCT
          fk.FK_NAME,
          STUFF((SELECT ','+ fk2.PKCOLUMN_NAME
                  FROM (SELECT FK_NAME, '[' + PKCOLUMN_NAME +']' PKCOLUMN_NAME FROM #ForeignKeys) fk2
                 WHERE FK.FK_NAME = fk2.FK_NAME
              GROUP BY fk2.PKCOLUMN_NAME
               FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '') FieldList
    FROM #ForeignKeys fk
    ) PKlist
ON fk.FK_NAME = PKlist.FK_NAME
INNER JOIN
    (
    SELECT DISTINCT
          fk.FK_NAME,
          STUFF((SELECT ','+ fk2.FKCOLUMN_NAME
                  FROM (SELECT FK_NAME, '[' + FKCOLUMN_NAME + ']' FKCOLUMN_NAME FROM #ForeignKeys) fk2
                 WHERE FK.FK_NAME = fk2.FK_NAME
              GROUP BY fk2.FKCOLUMN_NAME
               FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '') FieldList
    FROM #ForeignKeys fk
    ) FKlist
ON fk.FK_NAME = FKlist.FK_NAME

DROP TABLE #ForeignKeys



SELECT * FROM #FKScripts


/* OK, let's disable these foreign keys, going to have to use a cursor for this (ouch) */

DECLARE @disablesql nvarchar(max)

DECLARE discur CURSOR LOCAL FOR
    SELECT FKDisableScript FROM #FKScripts 

OPEN discur

FETCH NEXT FROM discur INTO @disablesql

WHILE @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    EXEC sp_executesql @disablesql

    FETCH NEXT FROM discur INTO @disablesql
END

CLOSE discur
DEALLOCATE discur

/* right, we're finished with the cursor that disables the foreign keys, phew! */


/* check that the constraints are now disabled (for testing) */
SELECT DISTINCT
fkt.*
,CASE WHEN ((C.Status & 0x4000)) = 0 THEN 1 ELSE 0 END [Enabled2]
FROM #FKScripts fkt
INNER JOIN sys.sysobjects o2
    ON o2.name = fkt.FKName
INNER JOIN sys.sysobjects o 
    ON o2.id = o.parent_obj
    AND o.xtype='F'
INNER JOIN sys.sysconstraints c
    ON  o.id = c.constid  

    select * from #FKScripts

然后你需要删除所有索引;

/* Drop index scripts into a temp table */

IF OBJECT_ID('tempdb..#CreateIndexes') IS NOT NULL DROP TABLE #CreateIndexes
GO

DECLARE @SchemaName varchar(100)
DECLARE @TableName varchar(256)
DECLARE @IndexName varchar(256)
DECLARE @ColumnName varchar(100)
DECLARE @is_unique varchar(100)
DECLARE @IndexTypeDesc varchar(100)
DECLARE @FileGroupName varchar(100)
DECLARE @is_disabled bit
DECLARE @is_primary_key bit
DECLARE @IndexOptions varchar(max)
DECLARE @IndexColumnId int
DECLARE @IsDescendingKey int 
DECLARE @IsIncludedColumn int
DECLARE @TSQLScripCreationIndex varchar(max)
DECLARE @TSQLScripDisableIndex varchar(max)

CREATE TABLE #CreateIndexes (
                            SchemaName varchar(max)
                            ,TableName varchar(max)
                            ,IndexName varchar(max)
                            ,is_unique varchar(max)
                            ,IndexTypeDesc varchar(max)
                            ,is_disabled bit
                            ,is_primary_key bit
                            ,FileGroupName varchar(max)
                            ,DropScript varchar(max)
                            ,TSQLScripCreationIndex varchar(max)
                            ,TSQLScripDisableIndex varchar(max)
                            )

DECLARE CursorIndex CURSOR FOR
 SELECT schema_name(t.schema_id) [schema_name]
 , t.name
 , ix.name
 , CASE WHEN ix.is_unique = 1 THEN 'UNIQUE ' ELSE '' END 
 , ix.type_desc
 , '' IndexOptions -- case when ix.is_padded=1 then 'PAD_INDEX = ON, ' else 'PAD_INDEX = OFF, ' end
    --+ case when ix.allow_page_locks=1 then 'ALLOW_PAGE_LOCKS = ON, ' else 'ALLOW_PAGE_LOCKS = OFF, ' end
    --+ case when ix.allow_row_locks=1 then  'ALLOW_ROW_LOCKS = ON, ' else 'ALLOW_ROW_LOCKS = OFF, ' end
    --+ case when INDEXPROPERTY(t.object_id, ix.name, 'IsStatistics') = 1 then 'STATISTICS_NORECOMPUTE = ON, ' else 'STATISTICS_NORECOMPUTE = OFF, ' end
    --+ case when ix.ignore_dup_key=1 then 'IGNORE_DUP_KEY = ON, ' else 'IGNORE_DUP_KEY = OFF, ' end
    --+ 'SORT_IN_TEMPDB = OFF, FILLFACTOR = ' + CASE WHEN ix.fill_factor = 0 THEN '90' ELSE CAST(ix.fill_factor AS VARCHAR(3))  END AS IndexOptions

 , ix.is_disabled 
 , ix.is_primary_key
 , FILEGROUP_NAME(ix.data_space_id) FileGroupName
 FROM sys.tables t 
 INNER JOIN sys.indexes ix on t.object_id=ix.object_id
 INNER JOIN #FKScripts fks ON t.name = fks.FKTableName
 WHERE ix.type>0 
    AND t.is_ms_shipped=0 
    AND t.name<>'sysdiagrams' 
    --and t.name = 'Entity'
 ORDER BY schema_name(t.schema_id), t.name, ix.name

OPEN CursorIndex
FETCH NEXT FROM CursorIndex INTO  @SchemaName, @TableName, @IndexName, @is_unique, @IndexTypeDesc, @IndexOptions,@is_disabled, @is_primary_key, @FileGroupName

WHILE (@@fetch_status=0)
BEGIN
DECLARE @IndexColumns varchar(max)
DECLARE @IncludedColumns varchar(max)

SET @IndexColumns=''
SET @IncludedColumns=''

DECLARE CursorIndexColumn CURSOR FOR 
SELECT 
col.name
,ixc.is_descending_key
,ixc.is_included_column
FROM sys.tables tb 
INNER JOIN sys.indexes ix 
  ON tb.object_id=ix.object_id
INNER JOIN sys.index_columns ixc 
  ON ix.object_id=ixc.object_id 
  AND ix.index_id= ixc.index_id
INNER JOIN sys.columns col 
  ON ixc.object_id = col.object_id
  AND ixc.column_id = col.column_id
WHERE ix.type>0 
  AND (ix.is_primary_key=0 or ix.is_unique_constraint=0)
  AND schema_name(tb.schema_id) = @SchemaName 
  AND tb.name = @TableName 
  AND ix.name = @IndexName
ORDER BY ixc.index_column_id

 OPEN CursorIndexColumn 
 FETCH NEXT FROM CursorIndexColumn INTO  @ColumnName, @IsDescendingKey, @IsIncludedColumn

 WHILE (@@fetch_status=0)
 BEGIN
  IF @IsIncludedColumn=0 
   SET @IndexColumns=@IndexColumns + @ColumnName  + CASE WHEN @IsDescendingKey=1 THEN ' DESC, ' ELSE  ' ASC, ' END
  ELSE 
   SET @IncludedColumns=@IncludedColumns  + @ColumnName  +', ' 

  FETCH NEXT FROM CursorIndexColumn INTO @ColumnName, @IsDescendingKey, @IsIncludedColumn
 END

 CLOSE CursorIndexColumn
 DEALLOCATE CursorIndexColumn

 SET @IndexColumns = substring(@IndexColumns, 1, len(@IndexColumns)-1)
 SET @IncludedColumns = CASE WHEN len(@IncludedColumns) >0 THEN substring(@IncludedColumns, 1, len(@IncludedColumns)-1) ELSE '' END
 --  print @IndexColumns
 --  print @IncludedColumns

 SET @TSQLScripCreationIndex =''
 SET @TSQLScripDisableIndex =''
 SET @TSQLScripCreationIndex=
 CASE    WHEN @is_primary_key = 1 THEN 'ALTER TABLE ' + QUOTENAME(@SchemaName) +'.'+ QUOTENAME(@TableName) + ' ADD CONSTRAINT ' + QUOTENAME(@IndexName) + ' PRIMARY KEY CLUSTERED (' + @IndexColumns + ' )' + @IndexOptions + ' ON ' + QUOTENAME(@FileGroupName) + ';'  
         ELSE 'CREATE '+ @is_unique  +@IndexTypeDesc + ' INDEX ' +QUOTENAME(@IndexName)+' ON ' + QUOTENAME(@SchemaName) +'.'+ QUOTENAME(@TableName)+ ' ('+@IndexColumns+') '
 + CASE WHEN len(@IncludedColumns)>0 THEN 'INCLUDE (' + @IncludedColumns+ ')' ELSE '' END + CHAR(13) --+'WITH (' + @IndexOptions+ ') ON ' + QUOTENAME(@FileGroupName) + ';'  
  END

 --if @is_disabled=1 
  SET  @TSQLScripDisableIndex=  CASE WHEN @IndexTypeDesc = 'CLUSTERED' THEN 'ALTER TABLE ' + QUOTENAME(@SchemaName) +'.'+ QUOTENAME(@TableName) + ' DROP CONSTRAINT ' + QUOTENAME(@IndexName)
                                        ELSE 'DROP INDEX  ' + QUOTENAME(@IndexName) + ' ON ' + QUOTENAME(@SchemaName) +'.'+ QUOTENAME(@TableName) + CHAR(13) END

 PRINT @TSQLScripCreationIndex
 PRINT @TSQLScripDisableIndex

 INSERT INTO #CreateIndexes (SchemaName, TableName, IndexName, is_unique, IndexTypeDesc, is_disabled, is_primary_key ,FileGroupName, TSQLScripCreationIndex, TSQLScripDisableIndex)
 SELECT @SchemaName, @TableName, @IndexName, @is_unique, @IndexTypeDesc, @is_disabled, @is_primary_key, @FileGroupName, @TSQLScripCreationIndex, @TSQLScripDisableIndex

 FETCH NEXT FROM CursorIndex INTO  @SchemaName, @TableName, @IndexName, @is_unique, @IndexTypeDesc, @IndexOptions,@is_disabled, @is_primary_key ,@FileGroupName

END
CLOSE CursorIndex
DEALLOCATE CursorIndex

SELECT * FROM #CreateIndexes ORDER BY TableName, IndexName

如果你有全文索引,那么你需要这个;

/* NEED TO DISABLE FULL TEXT INDEXES ON ANY OF THESE TABLES  - AAARRRGGGGGHHHHHH!!!! */

IF OBJECT_ID('tempdb..#FullTextCreateScripts') IS NOT NULL DROP TABLE #FullTextCreateScripts
CREATE TABLE #FullTextCreateScripts (TableName sysname, ColumnName sysname, FullTextCreateScript nvarchar(max))
INSERT INTO #FullTextCreateScripts (TableName, ColumnName, FullTextCreateScript)
SELECT 
o.name TableName
,c.name ColumnName
,'ADD FULLTEXT INDEX ON ' + o.name + 'ADD (' + c.name + ')' FullTextCreateScript
FROM sys.fulltext_index_columns fic
LEFT JOIN sys.objects o
    ON o.object_id = fic.object_id
LEFT JOIN sys.columns c
    ON c.object_id = fic.object_id
    AND c.column_id = fic.column_id
INNER JOIN #FKScripts fks
    ON o.name = fks.FKTableName


SELECT * FROM #FullTextCreateScripts


IF OBJECT_ID('tempdb..#FullTextDropScripts') IS NOT NULL DROP TABLE #FullTextDropScripts
CREATE TABLE #FullTextDropScripts (TableName sysname, FullTextDropScript nvarchar(max))
INSERT INTO #FullTextDropScripts (TableName, FullTextDropScript)
SELECT DISTINCT
o.name TableName
,'DROP FULLTEXT INDEX ON ' + o.name FullTextDropScript
FROM sys.fulltext_index_columns fic
LEFT JOIN sys.objects o
    ON o.object_id = fic.object_id
INNER JOIN #FKScripts fks
    ON o.name = fks.FKTableName


SELECT * FROM #FullTextDropScripts

/* Another cursor, this one drops our relevant full text indexes */

DECLARE @dropfulltextSQL nvarchar(max)

DECLARE dropfulltextcursor CURSOR LOCAL FOR
    SELECT FullTextDropScript FROM #FullTextDropScripts 

OPEN dropfulltextcursor

FETCH NEXT FROM dropfulltextcursor INTO @dropfulltextSQL

WHILE @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    EXEC sp_executesql @dropfulltextSQL

    FETCH NEXT FROM dropfulltextcursor INTO @dropfulltextSQL
END

CLOSE dropfulltextcursor
DEALLOCATE dropfulltextcursor

/ *让我们使用游标* /

删除这些索引
DECLARE @dropindexes nvarchar(max)

DECLARE dropindex CURSOR LOCAL FOR
    SELECT TSQLScripDisableIndex FROM #CreateIndexes 

OPEN dropindex

FETCH NEXT FROM dropindex INTO @dropindexes

WHILE @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    EXEC sp_executesql @dropindexes

    FETCH NEXT FROM dropindex INTO @dropindexes
END

CLOSE dropindex
DEALLOCATE dropindex

这就是你想要你的ALTER SCRIPT的地方

一旦你完成了改变,然后开始重新应用你已经丢弃的所有内容;

/ *让我们将这些全文索引添加回* /

DECLARE @addfulltextSQL nvarchar(max)

DECLARE createfulltextcursor CURSOR LOCAL FOR
    SELECT FullTextCreateScript FROM #FullTextCreateScripts 

OPEN createfulltextcursor

FETCH NEXT FROM createfulltextcursor INTO @addfulltextSQL

WHILE @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    EXEC sp_executesql @addfulltextSQL

    FETCH NEXT FROM createfulltextcursor INTO @addfulltextSQL
END

CLOSE createfulltextcursor
DEALLOCATE createfulltextcursor


/* Rebuild those indexes */


DECLARE @createindexes nvarchar(max)

DECLARE createindexes CURSOR LOCAL FOR
    SELECT TSQLScripCreationIndex FROM #CreateIndexes 

OPEN createindexes

FETCH NEXT FROM createindexes INTO @createindexes

WHILE @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    EXEC sp_executesql @createindexes

    FETCH NEXT FROM createindexes INTO @createindexes
END

CLOSE createindexes
DEALLOCATE createindexes




/* Now let's re-enable those FK constraints */

DECLARE @enablesql nvarchar(max)
DECLARE @checksql nvarchar(max)

DECLARE enacur CURSOR LOCAL FOR
    SELECT FKRebuildScript, FKCheckScript FROM #FKScripts 

OPEN enacur

FETCH NEXT FROM enacur INTO @enablesql, @checksql

WHILE @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    EXEC sp_executesql @enablesql
    EXEC sp_executesql @checksql

    FETCH NEXT FROM enacur INTO @enablesql, @checkSQL
END

CLOSE enacur
DEALLOCATE enacur





/* Let's check that the constraints are now enabled again (for testing) */
SELECT DISTINCT
fkt.*
,CASE WHEN ((C.Status & 0x4000)) = 0 THEN 1 ELSE 0 END [Enabled3]
FROM #FKScripts fkt
INNER JOIN sys.sysobjects o2
    ON o2.name = fkt.PKName
INNER JOIN sys.sysobjects o 
    ON o2.id = o.parent_obj
    AND o.xtype='F'
INNER JOIN sys.sysconstraints c
    ON  o.id = c.constid