Sql Server中同步两个数据库表的数据结构(转)

发布时间 2024-01-12 13:55:20作者: EasyBI

RT

转自 Sql Server中同步两个数据库表的数据结构_sqlserver同步表结构-CSDN博客 

 

------------------------------------------------------------------------------
--功能:源数据库和本地目标数据库中同步表,如有增加的列,在此备库中也自动增加列
--##UserTables_Source    : 存放来源数据库用户表和表中字段
--##UserTables            : 存放本地数据库用户表和表中字段
--##UserTables_Tab        : 在来源数据库存在的(用户表)而本地不存在,则执行建新表,只建表结构
--##UserTables_Col        : 在来源数据库存在的(字段)而本地不存在,则执行添加字段
--需一键替换数据数据库名称"[源数据库名称]"改为源数据库
--created by lilijie
----------------------------------------------------------------------------------------
--'bit','datetime','int','real','uniqueidentifier','timestamp','smallint','float' 

        declare @TableName nvarchar(200)
        declare @PkCol nvarchar(50)
        declare @PkName nvarchar(200) 
        declare @PkCols nvarchar(max)
        declare @PkColsDrop nvarchar(max)
        --if not exists (select * from sys.servers where is_linked=1 and name='连接服务器名')
        --BEGIN 
        --    EXEC  sp_addlinkedserver @server = N'连接服务器名', @srvproduct=N'', @provider=N'SQLNCLI11', @datasrc=N'192.168.2.23', @catalog=N''
        --    EXEC  sp_addlinkedsrvlogin @rmtsrvname=N'连接服务器名',@useself=N'False',@locallogin='sa',@rmtuser='sa',@rmtpassword='sa_password'
        --END 
        --"[源数据库名称]" 是来源数据库,如来源数据库名为"[源数据库名称]",需全部替换
        --来源数据库用户表和字段的数据类型
        IF object_id(N'tempdb..##UserTables_Source',N'U') IS NOT NULL 
            DROP TABLE ##UserTables_Source
        SELECT a.name AS table_name,b.name AS  column_name,c.name AS  column_type,CASE WHEN  b.length = -1 THEN '(max)'  WHEN c.name in ('nvarchar') THEN '('+CAST(b.prec AS VARCHAR ) +')' WHEN c.name in ('decimal') then '('+CAST(b.prec AS VARCHAR ) + ','+ CAST(b.scale AS VARCHAR )+')' ELSE '' END  AS column_length,b.colid 
        INTO ##UserTables_Source 
        FROM "[源数据库名称]".sys.sysobjects a,"[源数据库名称]".dbo.syscolumns b ,"[源数据库名称]".dbo.systypes c 
        WHERE a.id = b.id AND b.xtype = c.xusertype AND a.xtype = 'U' 
        ORDER BY a.name,b.colid 
         
        Lable:
        --目标数据库用户表(即当前数据库)
        IF object_id(N'tempdb..##UserTables',N'U') IS NOT NULL
            DROP TABLE ##UserTables 
        SELECT a.name AS table_name,b.name AS  column_name,c.name AS  column_type,CASE WHEN  b.length = -1 THEN '(max)'  WHEN c.name in ('nvarchar') THEN '('+CAST(b.prec AS VARCHAR ) +')' WHEN c.name in ('decimal') then '('+CAST(b.prec AS VARCHAR ) + ','+ CAST(b.scale AS VARCHAR )+')' ELSE '' END  AS column_length,b.colid  
        INTO ##UserTables
        FROM sys.sysobjects a,syscolumns b ,systypes c 
        WHERE a.id = b.id AND b.xtype = c.xusertype AND a.xtype = 'U' 
        ORDER BY a.name,b.colid 
        
        IF object_id(N'tempdb..##UserTables_Tab ',N'U') IS NOT NULL
            DROP TABLE ##UserTables_Tab
        
        --源库中存在表,而目标库中不存在,则在目标库中添加表
        SELECT  DISTINCT 'SELECT * INTO '+ table_name + ' FROM "[源数据库名称]".dbo.' + table_name + ' WHERE 1=2' AS create_table,table_name AS TableName INTO ##UserTables_Tab   FROM ##UserTables_Source st
        WHERE NOT EXISTS (SELECT 1 FROM ##UserTables tt WHERE tt.table_name = st.table_name  )  
 
        --目标库存在表,而源库中不存在,则将目标库中表drop掉
        INSERT INTO ##UserTables_Tab(create_table,TableName)
        SELECT DISTINCT 'DROP TABLE ' + table_name  AS create_table,table_name AS TableName FROM ##UserTables tt
        WHERE NOT EXISTS (SELECT 1 FROM ##UserTables_Source  st WHERE tt.table_name = st.table_name)
 
        IF EXISTS (SELECT create_table FROM ##UserTables_Tab)     
            BEGIN
                DECLARE cur_create_table CURSOR FOR SELECT create_table,TableName FROM ##UserTables_Tab 
                DECLARE @create_table   nvarchar(max)
                OPEN cur_create_table
                FETCH NEXT FROM cur_create_table INTO @create_table,@TableName
                WHILE @@FETCH_STATUS  = 0
                    BEGIN 
                        print @create_table
                        EXEC(@create_table)--执行建表或drop表的动作
                        FETCH NEXT FROM cur_create_table INTO @create_table,@TableName
                    END
                CLOSE cur_create_table
                DEALLOCATE cur_create_table
                GOTO Lable 
            END
        
        --目标数据库用户表中字段名称和本地表中字段名称要一致
        IF object_id(N'tempdb..##UserTables_Col ',N'U') IS NOT NULL
            BEGIN
                DROP TABLE ##UserTables_Col
                GOTO Lable 
            END
            
        SELECT  DISTINCT 'ALTER TABLE '+ table_name + ' ADD ' + column_name +' '+ column_type + column_length AS alter_column  INTO ##UserTables_Col   FROM ##UserTables_Source st
        WHERE NOT EXISTS (SELECT 1 FROM ##UserTables tt WHERE tt.table_name = st.table_name AND tt.column_name = st.column_name  ) 
        
        --来源表中字段和本地表中字段数据类型和长度要一致
        INSERT  INTO ##UserTables_Col(alter_column ) 
        SELECT  DISTINCT 'ALTER TABLE '+ table_name + ' ALTER COLUMN ' + column_name +' '+ column_type + column_length AS add_column
        FROM ##UserTables_Source st
        WHERE NOT EXISTS (SELECT 1 FROM ##UserTables tt WHERE tt.table_name = st.table_name AND tt.column_name = st.column_name  and tt.column_type = st.column_type and tt.column_length = st.column_length ) 
        --select * from ##UserTables_Col
 
        --来源表中字段不存在,而本地表中字段存在,则删除本地表中字段
        INSERT  INTO ##UserTables_Col(alter_column )
        SELECT 'ALTER TABLE '+table_name + ' DROP COLUMN ' + column_name  FROM ##UserTables tt
        WHERE NOT EXISTS (SELECT 1 FROM ##UserTables_Source st WHERE tt.table_name = st.table_name AND tt.column_name = st.column_name )
 
        DECLARE @alter_column   nvarchar(max)
        IF EXISTS (SELECT alter_column FROM ##UserTables_Col)     
            BEGIN
                DECLARE cur_alter_column CURSOR FOR SELECT alter_column FROM ##UserTables_Col ORDER BY 1 ASC
                OPEN cur_alter_column
                FETCH NEXT FROM cur_alter_column INTO @alter_column
                WHILE @@FETCH_STATUS  = 0
                    BEGIN 
                        print @alter_column
                        EXEC(@alter_column)--执行添加字段的动作
                        FETCH NEXT FROM cur_alter_column INTO @alter_column
                    END
                CLOSE cur_alter_column
                DEALLOCATE cur_alter_column
            END
        
        IF object_id(N'tempdb..##UserTables_SourcePK ',N'U') IS NOT NULL
            DROP TABLE ##UserTables_SourcePK
        select tb.name TableName,t1.name PkCol ,t2.name PkName  -- 源库中表 主键
            into ##UserTables_SourcePK
        from "[源数据库名称]".dbo.SYSCOLUMNS t1
        left join "[源数据库名称]".dbo.SYSOBJECTS t2 on t2.parent_obj = t1.id AND t2.xtype = 'PK'
        left join "[源数据库名称]".dbo.SYSINDEXES t3 on t3.id = t1.id and t2.name = t3.name
        left join "[源数据库名称]".dbo.SYSINDEXKEYS t4 on t1.colid = t4.colid and t4.id = t1.id and t4.indid = t3.indid
        left join "[源数据库名称]".dbo.systypes t5 on t1.xtype=t5.xtype
        left join sys.extended_properties t6 on t1.id=t6.major_id and t1.colid=t6.minor_id
        left join "[源数据库名称]".dbo.SYSOBJECTS tb on tb.id=t1.id  and t5.name<>'sysname' and   t4.id is not null
        where tb.name in( select table_name from ##UserTables_Source)
 
        IF object_id(N'tempdb..##UserTablesPK ',N'U') IS NOT NULL
            DROP TABLE ##UserTablesPK
        select tb.name TableName,t1.name PkCol ,t2.name PkName  --目标库中表 主键
            into ##UserTablesPK
        from SYSCOLUMNS t1
        left join SYSOBJECTS t2 on t2.parent_obj = t1.id AND t2.xtype = 'PK'
        left join SYSINDEXES t3 on t3.id = t1.id and t2.name = t3.name
        left join SYSINDEXKEYS t4 on t1.colid = t4.colid and t4.id = t1.id and t4.indid = t3.indid
        left join systypes t5 on t1.xtype=t5.xtype
        left join sys.extended_properties t6 on t1.id=t6.major_id and t1.colid=t6.minor_id
        left join  SYSOBJECTS tb on tb.id=t1.id  and t5.name<>'sysname' and   t4.id is not null
        where tb.name in( select table_name from ##UserTables)
 
        DECLARE CurPk cursor for (select DISTINCT TableName    from ##UserTables_SourcePK a where not exists (select 1 from ##UserTablesPK b where a.TableName = b.TableName and a.PkCol = b.PkCol and a.PkName = b.PkName ) )
        OPEN CurPk
        FETCH NEXT FROM CurPk INTO @TableName 
        WHILE @@FETCH_STATUS = 0 
        BEGIN
            SET @PkCols= ''
            DECLARE CurPkDet CURSOR FOR (SELECT PkCol,PkName FROM ##UserTables_SourcePK WHERE TableName = @TableName )
            OPEN CurPkDet
            FETCH NEXT FROM CurPkDet INTO @PkCol,@PkName
            WHILE @@FETCH_STATUS = 0     
            BEGIN
                SET @PkCols = @PkCols + ',' +  @PkCol
                FETCH NEXT FROM CurPkDet INTO @PkCol,@PkName
            END 
            --DECLARE  @PkColsDrop NVARCHAR(MAX) = '' 
            SET @PkColsDrop = ( SELECT  DISTINCT  'ALTER TABLE '+ TableName + ' DROP CONSTRAINT ' + PkName AS PkColsDrop   FROM  ##UserTablesPK WHERE TableName = @TableName )
            IF @PkColsDrop IS NOT NULL 
                print @PkColsDrop
                EXEC (@PkColsDrop)  --删除旧的主键
 
            SET @PkCols = 'ALTER TABLE ' + @TableName  + ' ADD  CONSTRAINT [' + @PkName + '] PRIMARY KEY NONCLUSTERED ( ' + STUFF(@PkCols,1,1,'')  + ')'
            print @PkCols
            EXEC(@PkCols) 
            --SELECT @PkCols
            CLOSE CurPkDet
            DEALLOCATE CurPkDet            
            FETCH NEXT FROM CurPk INTO @TableName 
        END
        CLOSE CurPk
        DEALLOCATE CurPk
 
        DROP TABLE ##UserTables_Source
        DROP TABLE ##UserTables
        DROP TABLE ##UserTables_Tab
        DROP TABLE ##UserTables_Col
        DROP TABLE ##UserTables_SourcePK
        DROP TABLE ##UserTablesPK