SQL Server数据库自动处理外键关系的存储过程

最近总在做数据迁移的工作。做过类似工作的朋友都知道,数据迁移时,表之间的外键约束实在是让你头疼的事。因为你必须要确定表迁移的顺序,否则迁移过程中,总是在报外键约束的错误。即使你把表之间的外键临时drop了,迁移数据后,再create时,就会发现由于数据的不一致,导致外键已经无法创建成功了。

 

根据我的经验,如果迁移的数据不是那么重要的话或者说允许表中有些不一致数据存在的话,那么不必drop和create外键这么麻烦,可以使用no check语句,让外键临时失效,迁移后再启用。这样迁移后的表即使有外键不一致的数据,也不报错。

 

下面这个存储过程是根据表名,schema和操作方式,自动产生外键操作。这里要感谢Greg Robidoux,是他的一篇文章给了我启发,而且下面这个存储过程也是在他原先的存储过程基础上改造了。

[c-sharp] view plain copy print ?
  1. IF EXISTS (  
  2.   SELECT *   
  3.     FROM INFORMATION_SCHEMA.ROUTINES   
  4.    WHERE SPECIFIC_SCHEMA = N'dbo'  
  5.      AND SPECIFIC_NAME = N'USP_TJVictor_PKFK'   
  6. )  
  7.    DROP PROCEDURE USP_TJVictor_PKFK  
  8. GO  
  9. -- =============================================  
  10. -- Author:      TJVictor  
  11. -- Create date: 2009-12-29  
  12. -- Description: 指定表名和schema后,可产生指定表的外键脚本,  
  13. -- 操作类型为:ENABLE, DISABLE, DROP, CREATE, DROPANDCREATE  
  14. -- =============================================  
  15. CREATE PROCEDURE USP_TJVictor_PKFK  
  16.     @operation VARCHAR(32),    
  17.     @tableName sysname,   
  18.     @schemaName sysname   
  19. AS  
  20.     DECLARE @cmd NVARCHAR(max)   
  21.   
  22. DECLARE     
  23.    @FK_NAME sysname,    
  24.    @FK_OBJECTID INT,    
  25.    @FK_DISABLED INT,    
  26.    @FK_NOT_FOR_REPLICATION INT,    
  27.    @DELETE_RULE    smallint,       
  28.    @UPDATE_RULE    smallint,       
  29.    @FKTABLE_NAME sysname,    
  30.    @FKTABLE_OWNER sysname,    
  31.    @PKTABLE_NAME sysname,    
  32.    @PKTABLE_OWNER sysname,    
  33.    @FKCOLUMN_NAME sysname,    
  34.    @PKCOLUMN_NAME sysname,    
  35.    @CONSTRAINT_COLID INT    
  36.   
  37. DECLARE cursor_fkeys CURSOR FOR     
  38.    SELECT  Fk.name,    
  39.            Fk.OBJECT_ID,     
  40.            Fk.is_disabled,     
  41.            Fk.is_not_for_replication,     
  42.            Fk.delete_referential_action,     
  43.            Fk.update_referential_action,     
  44.            OBJECT_NAME(Fk.parent_object_id) AS Fk_table_name,     
  45.            schema_name(Fk.schema_id) AS Fk_table_schema,     
  46.            TbR.name AS Pk_table_name,     
  47.            schema_name(TbR.schema_id) Pk_table_schema    
  48.    FROM    sys.foreign_keys Fk LEFT OUTER JOIN     
  49.            sys.tables TbR ON TbR.OBJECT_ID = Fk.referenced_object_id --inner join     
  50.    WHERE   TbR.name = @tableName    
  51.            AND schema_name(TbR.schema_id) = @schemaName    
  52.   
  53. OPEN cursor_fkeys    
  54.   
  55. FETCH NEXT FROM   cursor_fkeys     
  56.    INTO @FK_NAME,@FK_OBJECTID,    
  57.        @FK_DISABLED,    
  58.        @FK_NOT_FOR_REPLICATION,    
  59.        @DELETE_RULE,       
  60.        @UPDATE_RULE,       
  61.        @FKTABLE_NAME,    
  62.        @FKTABLE_OWNER,    
  63.        @PKTABLE_NAME,    
  64.        @PKTABLE_OWNER    
  65.   
  66. WHILE @@FETCH_STATUS = 0     
  67. BEGIN     
  68.   
  69.    -- create statement for enabling FK    
  70.    IF @operation = 'ENABLE'     
  71.    BEGIN    
  72.        SET @cmd = 'ALTER TABLE [' + @FKTABLE_OWNER + '].[' + @FKTABLE_NAME     
  73.            + ']  CHECK CONSTRAINT [' + @FK_NAME + ']'    
  74.   
  75.       PRINT @cmd    
  76.    END    
  77.   
  78.    -- create statement for disabling FK    
  79.    IF @operation = 'DISABLE'    
  80.    BEGIN       
  81.        SET @cmd = 'ALTER TABLE [' + @FKTABLE_OWNER + '].[' + @FKTABLE_NAME     
  82.            + ']  NOCHECK CONSTRAINT [' + @FK_NAME + ']'    
  83.   
  84.       PRINT @cmd    
  85.    END    
  86.   
  87.    -- create statement for dropping FK and also for recreating FK    
  88.    IF @operation = 'DROP' OR @operation = 'DROPANDCREATE'  
  89.    BEGIN    
  90.   
  91.        -- drop statement    
  92.        SET @cmd = 'ALTER TABLE [' + @FKTABLE_OWNER + '].[' + @FKTABLE_NAME     
  93.        + ']  DROP CONSTRAINT [' + @FK_NAME + ']'       
  94.   
  95.       PRINT @cmd    
  96.     END  
  97.     IF @operation = 'CREATE' OR @operation = 'DROPANDCREATE'  
  98.     BEGIN   
  99.        -- create process    
  100.        DECLARE @FKCOLUMNS VARCHAR(1000), @PKCOLUMNS VARCHAR(1000), @COUNTER INT    
  101.   
  102.        -- create cursor to get FK columns    
  103.        DECLARE cursor_fkeyCols CURSOR FOR     
  104.        SELECT  COL_NAME(Fk.parent_object_id, Fk_Cl.parent_column_id) AS Fk_col_name,     
  105.                COL_NAME(Fk.referenced_object_id, Fk_Cl.referenced_column_id) AS Pk_col_name    
  106.        FROM    sys.foreign_keys Fk LEFT OUTER JOIN     
  107.                sys.tables TbR ON TbR.OBJECT_ID = Fk.referenced_object_id INNER JOIN     
  108.                sys.foreign_key_columns Fk_Cl ON Fk_Cl.constraint_object_id = Fk.OBJECT_ID     
  109.        WHERE   TbR.name = @tableName    
  110.                AND schema_name(TbR.schema_id) = @schemaName    
  111.                AND Fk_Cl.constraint_object_id = @FK_OBJECTID -- added 6/12/2008    
  112.        ORDER BY Fk_Cl.constraint_column_id    
  113.   
  114.        OPEN cursor_fkeyCols    
  115.   
  116.        FETCH NEXT FROM    cursor_fkeyCols INTO @FKCOLUMN_NAME,@PKCOLUMN_NAME    
  117.   
  118.        SET @COUNTER = 1    
  119.        SET @FKCOLUMNS = ''    
  120.        SET @PKCOLUMNS = ''    
  121.            
  122.        WHILE @@FETCH_STATUS = 0     
  123.        BEGIN     
  124.   
  125.            IF @COUNTER > 1     
  126.            BEGIN    
  127.                SET @FKCOLUMNS = @FKCOLUMNS + ','    
  128.                SET @PKCOLUMNS = @PKCOLUMNS + ','    
  129.            END    
  130.   
  131.            SET @FKCOLUMNS = @FKCOLUMNS + '[' + @FKCOLUMN_NAME + ']'    
  132.            SET @PKCOLUMNS = @PKCOLUMNS + '[' + @PKCOLUMN_NAME + ']'    
  133.   
  134.            SET @COUNTER = @COUNTER + 1    
  135.                
  136.            FETCH NEXT FROM    cursor_fkeyCols INTO @FKCOLUMN_NAME,@PKCOLUMN_NAME    
  137.        END    
  138.   
  139.        CLOSE cursor_fkeyCols     
  140.        DEALLOCATE cursor_fkeyCols     
  141.       
  142.        -- generate create FK statement    
  143.        SET @cmd = 'ALTER TABLE [' + @FKTABLE_OWNER + '].[' + @FKTABLE_NAME + ']  WITH ' +     
  144.            CASE @FK_DISABLED     
  145.                WHEN 0 THEN ' CHECK '    
  146.                WHEN 1 THEN ' NOCHECK '    
  147.            END +  ' ADD CONSTRAINT [' + @FK_NAME     
  148.            + '] FOREIGN KEY (' + @FKCOLUMNS     
  149.            + ') REFERENCES [' + @PKTABLE_OWNER + '].[' + @PKTABLE_NAME + '] ('     
  150.            + @PKCOLUMNS + ') ON UPDATE ' +     
  151.            CASE @UPDATE_RULE     
  152.                WHEN 0 THEN ' NO ACTION '    
  153.                WHEN 1 THEN ' CASCADE '     
  154.                WHEN 2 THEN ' SET NULL '     
  155.                END + ' ON DELETE ' +     
  156.            CASE @DELETE_RULE    
  157.                WHEN 0 THEN ' NO ACTION '     
  158.                WHEN 1 THEN ' CASCADE '     
  159.                WHEN 2 THEN ' SET NULL '     
  160.                END + '' +    
  161.            CASE @FK_NOT_FOR_REPLICATION    
  162.                WHEN 0 THEN ''    
  163.                WHEN 1 THEN ' NOT FOR REPLICATION '    
  164.            END    
  165.   
  166.       PRINT @cmd    
  167.   
  168.    END    
  169.   
  170.    FETCH NEXT FROM    cursor_fkeys     
  171.       INTO @FK_NAME,@FK_OBJECTID,    
  172.            @FK_DISABLED,    
  173.            @FK_NOT_FOR_REPLICATION,    
  174.            @DELETE_RULE,       
  175.            @UPDATE_RULE,       
  176.            @FKTABLE_NAME,    
  177.            @FKTABLE_OWNER,    
  178.            @PKTABLE_NAME,    
  179.            @PKTABLE_OWNER    
  180. END    
  181.   
  182. CLOSE cursor_fkeys     
  183. DEALLOCATE cursor_fkeys    
  184. GO  

使用方法:指定表名,schema(一般是dbo)和操作(ENABLE, DISABLE, DROP, CREATE, DROPANDCREATE)后,存储过程会根据操作,自动产生对应的代码,比如USP_TJVictor_PKFK 'DISABLE','TestTable','dbo',那么存储过程会自动打印出禁用dbo.TestTable外键的sql语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值