2016-08-23 64 views
4

我必須更改大量表格30+一列的數據類型從IntBigInt。 我的問題是,特定的列也是一個表PK和其他許多人FK。因此我無法更改,因爲我收到錯誤消息。如何使用腳本將列從一種數據類型更改爲antother

我需要一個算法或一些腳本。但我無法確定步驟或方法:我應該使用一個曲軸,還是僅僅使用臨時表從中刪除一條記錄,並始終得到第一條記錄...

我應該怎麼做?從堆棧中取出第一個表格,檢查所涉及的列是否被PK或FK引用,如果是,則刪除引用(PK,FK等),然後改變列類型,然後重新創建Pk和FKs?我應該存儲我摻雜的東西嗎?我很困惑...

任何提示?

+0

只是有一個嘗試:導入數據庫到數據庫項目在VS,修改列的數據類型,看看VS會產生什麼樣的腳本。它需要安裝SQL Server數據工具。 – qxg

+0

@qxg:謝謝,我已經在使用VS與SSDT。我會試試 – BogdanM

回答

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 @[email protected] + @ColumnName + CASE WHEN @IsDescendingKey=1 THEN ' DESC, ' ELSE ' ASC, ' END 
    ELSE 
    SET @[email protected] + @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 [email protected] + ' INDEX ' +QUOTENAME(@IndexName)+' ON ' + QUOTENAME(@SchemaName) +'.'+ QUOTENAME(@TableName)+ ' ('[email protected]+') ' 
+ 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 

/*讓我們放下使用遊標這些索引*/

​​

在這裏,你會想要做你的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 
+0

很有意思,謝謝。這正是我所期望的,但還沒有那麼遠。謝謝。 – BogdanM

+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列,因爲您還需要重新創建這些列。

而且,和往常一樣,首先在開發環境中測試一下,以確保您沒有跳過任何東西。

+0

感謝Radu的洞察力,但我很幸運,所有表格都被截斷,因此我可以在沒有這個額外問題的情況下重新創建它們。 – BogdanM

+0

@BogdanM如果表格是空的,那麼這很棒。在表格上是否創建了索引? –

+0

是的,至少對於一些有聚集索引 – BogdanM

相關問題