sqlserver表結構(含約束)複製儲存過程
阿新 • • 發佈:2019-01-31
上文中介紹了SQL Server中各種約束以及使用sql查詢各種約束的方法,本文基於上文實現了表結構
(含約束)複製的儲存過程。該儲存過程在SQL Server 2008 及 SQL Server 2014上測試可行。sql如下
/************************************************************ * 表結構複製(含約束)儲存過程 * Time: 2017/7/23 19:54:38 ************************************************************/ if object_id(N'sp_copy_table' ,N'P') is not null drop procedure sp_copy_table; go create procedure sp_copy_table @srcTableName varchar(200), @dstTableName varchar(220) as set nocount on begin try -- 如果源表不存在,則丟擲異常 declare @tabID varchar(30) = object_id(@srcTableName ,N'U'); if @tabID is null raiserror('src table not exists' ,16 ,1); -- 如果目標表已經存在,則丟擲異常 if object_id(@dstTableName ,N'U') is not null raiserror('destination table already exists' ,16 ,1); -- 建立表(不復制資料) declare @createSql varchar(max) = ''; set @createSql = 'SELECT * INTO ' + @dstTableName + ' FROM ' + @srcTableName + ' WHERE 1 > 1'; exec (@createSql); -- ============== 新增約束 ================ -- -- ============= 1. unique constraint / primary constraint ============= declare @tb1 table (IdxName varchar(255) ,colName varchar(255) ,consType tinyint) declare @tb2 table (IdxName varchar(255) ,colName varchar(255) ,consType tinyint) -- 查詢原表的主鍵約束、唯一約束(統一約束可能作用與多列上,結果集中作為多列) insert into @tb1 select idx.name as idxName ,col.name as colName ,(case when idx.is_primary_key = 1 then 1 when idx.is_unique_constraint = 1 then 2 else 0 end) consType from sys.indexes idx join sys.index_columns idxCol on ( idx.object_id = idxCol.object_id and idx.index_id = idxCol.index_id and (idx.is_unique_constraint = 1 or idx.is_primary_key = 1) ) join sys.columns col on (idx.object_id = col.object_id and idxCol.column_id = col.column_id) where idx.[object_id] = @tabID -- 按照約束名,將同一約束的多行結果集合併為一行,寫入臨時表 insert into @tb2 select idxName ,colsName = stuff( ( select ',' + colName from @tb1 where IdxName = a.idxName and consType = a.consType for xml path('') ) ,1 ,1 ,'' ) ,a.consType from @tb1 a; -- @tb1 臨時表資料已經沒用,刪除 delete from @tb1; -- 迴圈遍歷約束,寫入目標表 declare @checkName varchar(255) ,@colName varchar(255) ,@consType varchar(255) ,@tmp varchar(max); while exists( select 1 from @tb2 ) begin select @checkName = IdxName ,@colName = colName ,@consType = consType from @tb2; -- 主鍵約束 if @consType = 1 begin set @tmp = 'ALTER TABLE ' + @dstTableName + ' ADD CONSTRAINT ' + @checkName + '_01' + ' PRIMARY KEY (' + @colName + ')'; exec (@tmp); end-- 唯一約束 else if @consType = 2 begin set @tmp = 'ALTER TABLE ' + @dstTableName + ' ADD CONSTRAINT ' + @checkName + '_01' + ' UNIQUE (' + @colName + ')'; exec (@tmp); end -- 使用完後,刪除 delete from @tb2 where IdxName = @checkName and colName = @colName; end -- ================= 2. 外來鍵約束 =================== declare @tb3 table (fkName varchar(255) ,colName varchar(255) ,referTabName varchar(255), referColName varchar(255)) -- 查詢源表外來鍵約束,寫入臨時表 insert into @tb3 select fk.name as fkName ,SubCol.name as colName ,oMain.name as referTabName ,MainCol.name as referColName from sys.foreign_keys fk join sys.all_objects oSub on (fk.parent_object_id = oSub.object_id) join sys.all_objects oMain on (fk.referenced_object_id = oMain.object_id) join sys.foreign_key_columns fkCols on (fk.object_id = fkCols.constraint_object_id) join sys.columns SubCol on (oSub.object_id = SubCol.object_id and fkCols.parent_column_id = SubCol.column_id) join sys.columns MainCol on (oMain.object_id = MainCol.object_id and fkCols.referenced_column_id = MainCol.column_id) where oSub.[object_id] = @tabID; -- 遍歷每一個外來鍵約束,寫入目標表 declare @referTabName varchar(255) ,@referColName varchar(255); while exists( select 1 from @tb3 ) begin select @checkName = fkName ,@colName = colName ,@referTabName = referTabName ,@referColName = referColName from @tb3; set @tmp = 'ALTER TABLE ' + @dstTableName + ' ADD CONSTRAINT ' + @checkName + '_01' + ' FOREIGN KEY (' + @colName + ') REFERENCES ' + @referTabName + '(' + @referColName + ')'; exec (@tmp); delete from @tb3 where fkName = @checkName; end -- =============== 3.CHECK約束 =================== declare @tb4 table (checkName varchar(255) ,colName varchar(255) , definition varchar(max)); insert into @tb4 select chk.name as checkName ,col.name as colName ,chk.definition from sys.check_constraints chk join sys.columns col on (chk.parent_object_id = col.object_id and chk.parent_column_id = col.column_id) where chk.parent_object_id = @tabID -- 遍歷每一個CHECK約束,為目標表新增約束 declare @definition varchar(max); while exists( select 1 from @tb4 ) begin select @checkName = checkName ,@colName = colName ,@definition = [definition] from @tb4; set @tmp = 'ALTER TABLE ' + @dstTableName + ' ADD CONSTRAINT ' + @checkName + '_01' + ' CHECK ' + @definition; exec (@tmp); delete from @tb4 where checkName = @checkName; end -- ================ 4. default約束 ===================== insert into @tb4 select df.name as checkName ,c.name as colName ,df.definition from sys.default_constraints df join sys.[columns] as c on df.parent_column_id = c.column_id and df.parent_object_id = c.[object_id] where df.parent_object_id = @tabID; -- 遍歷每一個default 約束 while exists( select 1 from @tb4 ) begin select @checkName = checkName ,@colName = colName ,@definition = [definition] from @tb4; set @tmp = 'ALTER TABLE ' + @dstTableName + ' ADD CONSTRAINT ' + @checkName + '_01' + ' DEFAULT ' + @definition + ' FOR ' + @colName; print 'default: ' + @tmp; exec (@tmp); delete from @tb4 where checkName = @checkName; end end try begin catch -- 輸出錯誤資訊 select error_number() as ErrorNumber ,error_severity() as ErrorSeverity ,error_state() as ErrorState ,error_procedure() as ErrorProcedure ,error_line() as ErrorLine ,error_message() as ErrorMessage end catch
儲存過程定義好之後,就可以呼叫該儲存過程進行表結構複製了。這裡使用ReportServer資料庫的 Schedule表做測試
use ReportServer
exec dbo.sp_copy_table
@srcTableName = '[dbo].Schedule'
,@dstTableName = '[dbo].Schedule_bak'
exec sp_help 'dbo.Schedule_bak'
可以發現,已經成功複製了ReportServer資料庫的 Schedule表
注意事項:
1. 由於該儲存過程中使用了所在資料庫的一些系統表,不同的資料庫所含的系統表的資料不一致,
該儲存過程不支援跨資料庫複製表結構。使用時務必在需要進行表複製的資料庫新建該儲存過程,
再呼叫儲存過程進行表結構複製
2. 上面ReportServer資料庫的Schedule表進行表結構複製時,會有如下警告:
警告! 最大鍵長度為 900 個位元組。索引 'IX_Schedule_01' 的最大長度為 1040 個位元組。
對於某些大值組合,插入/更新操作將失敗。
這個是由於Schedule表的唯一約束含有兩個長度為520的varchar列,導致最大長度超出鍵的最大長度900導致的
3. 使用 select into 複製表結構時,如果原資料表內有各類約束(預設約束,唯一值約束等等)則會因為約束名重複
而導致約束丟失。因此,這裡使用相關表查詢源表的約束,然後在約束名後新增字尾之後再設定到新建的目標表