比对不同服务器,同环境,库结构差异-SQL Server版本

一、创建过程

USE [TEMPDB]
GO
-- =============================================
-- Author:                <LE>
-- Create date: <2017-06-28>
-- Description:        <比较两个实例之间的库结构差异>
-- 本地缺失:不处理
-- 本地比远程多:记录下来差异,并插入临时表(包括库及表)
-- 本地与远程类型不一致:记录下来差异,并插入临时表

-- 2018-04-19更新内容
-- 1. 修正表结构差异结果列名显示成表名的问题
-- 2. 增加字段类型代码转换,以明文显示差异列类型
-- =============================================
CREATE PROCEDURE [dbo].[PR_DB_STRUCT_COMPARE]
        (
                @DBNAME_PREFIX VARCHAR(300),--数据库前缀
                @TARGET_LINKED_SERVER_NAME VARCHAR(300)--目标连接服务器名
        )
AS
BEGIN
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;

        --定义变量
        DECLARE 
        @SSQL VARCHAR(1500),
        @DBNAME VARCHAR(300)
        
        --定义临时表
        --库差异
        CREATE TABLE #TMP_DB_DIFF(
                C_DBNAME VARCHAR(300) --库名
        )
        --表差异
        CREATE TABLE #TMP_TABLE_DIFF(
                C_DBNAME VARCHAR(300), --库名
                C_TBNAME VARCHAR(300) --表名
        )
        --字段差异
        CREATE TABLE #TMP_COLUMN_DIFF(
                N_DBFLAG INT, --库标记 1源库 2目标库
                C_DBNAME VARCHAR(300), --库名
                C_TBNAME VARCHAR(300), --表名
                C_COLNAME VARCHAR(300), --字段名
                N_TYPENAME VARCHAR(300), --类型名称
                N_COLLENGTH INT --字段长度
        )
        
        BEGIN TRY
        
        --插入本地多出来的库,不处理,后续自动处理
        --因为要拼接参数,所以不能直接执行,用动态SQL处理
        SET @SSQL = ' INSERT INTO #TMP_DB_DIFF '
                + ' SELECT S.NAME FROM MASTER..SYSDATABASES S '
                + ' LEFT JOIN '
                + @TARGET_LINKED_SERVER_NAME 
                + '.MASTER.DBO.SYSDATABASES T ON S.NAME = T.NAME '
                + ' WHERE T.NAME IS NULL '
        EXECUTE(@SSQL)

        
        --1. 定义游标 查询出本地及远程实例都有的数据库,后续逐个处理
        DECLARE DBS CURSOR FOR SELECT NAME FROM MASTER..SYSDATABASES 
                WHERE NAME LIKE @DBNAME_PREFIX + '%' AND NAME NOT IN (SELECT C_DBNAME FROM #TMP_DB_DIFF)

        --2. 打开游标
        OPEN DBS
        --3. 获取记录
        FETCH DBS INTO @DBNAME
        --4. 循环处理每个库
        WHILE @@FETCH_STATUS = 0
        BEGIN
          --4.1 开始处理每个库
          --4.1.1 处理差异表 得到本地有,但远程实例没有的表名,并把结果集插入临时表
          SET @SSQL = 'WITH S AS (SELECT NAME FROM ' + @DBNAME + '..SYSOBJECTS ' 
            + ' WHERE XTYPE = ''U''), '
            + ' T AS (SELECT NAME FROM ' + @TARGET_LINKED_SERVER_NAME + '.' + @DBNAME 
            + '.DBO.SYSOBJECTS WHERE XTYPE = ''U'') '
            + ' INSERT INTO #TMP_TABLE_DIFF '
            + ' SELECT ''' + @DBNAME + ''', S.NAME FROM S LEFT JOIN T ON S.NAME = T.NAME WHERE T.NAME IS NULL '
          --PRINT @SSQL
          --PRINT '------------'
          EXECUTE(@SSQL)
          
          --4.1.2 处理差异字段 得到本地有,但远程实例没有的列名,并把结果集插入临时表
          SET @SSQL = 'WITH S AS ( ' 
                + ' SELECT OBJS.name TBNAME, COLS.name COLNAME, '
                + ' CASE WHEN TYPS.name IS NULL THEN ''未知类型'' ELSE TYPS.name END COLTYPE, COLS.length COLLEN FROM ' 
                + @DBNAME + '..SYSOBJECTS OBJS ' 
                + ' INNER JOIN ' + @DBNAME + '..SYSCOLUMNS COLS ON OBJS.ID = COLS.ID '
                + ' LEFT JOIN ' + @DBNAME + '..SYSTYPES TYPS ON COLS.XTYPE = TYPS.XTYPE '
                + ' WHERE OBJS.XTYPE = ''U''), '
                + ' T AS ( '
                + ' SELECT OBJS.name TBNAME, COLS.name COLNAME, '
                + ' CASE WHEN TYPS.name IS NULL THEN ''未知类型'' ELSE TYPS.name END COLTYPE, COLS.length COLLEN FROM '
                + @TARGET_LINKED_SERVER_NAME + '.' + @DBNAME + '.DBO.SYSOBJECTS OBJS '
                + ' INNER JOIN ' + @TARGET_LINKED_SERVER_NAME + '.' + @DBNAME 
                + '.DBO.SYSCOLUMNS COLS ON OBJS.ID = COLS.ID '
                + ' LEFT JOIN ' + @TARGET_LINKED_SERVER_NAME + '.' + @DBNAME +  '.DBO.SYSTYPES TYPS ON COLS.XTYPE = TYPS.XTYPE '
                + ' WHERE OBJS.XTYPE = ''U'') '
                + ' INSERT INTO #TMP_COLUMN_DIFF '
                + ' SELECT CASE WHEN S.TBNAME IS NULL THEN 1 ELSE 2 END N_DBFLAG, ''' + @DBNAME
                + ''' , CASE WHEN S.TBNAME IS NULL THEN T.TBNAME ELSE S.TBNAME END TBNAME, '
                + ' CASE WHEN S.COLNAME IS NULL THEN T.COLNAME ELSE S.COLNAME END COLNAME, '
                + ' CASE WHEN S.COLTYPE IS NULL THEN T.COLTYPE ELSE S.COLTYPE END COLTYPE, '
                + ' CASE WHEN S.COLLEN IS NULL THEN T.COLLEN ELSE S.COLLEN END COLLEN '
                + ' FROM S '
                + ' FULL JOIN T ON S.TBNAME = T.TBNAME AND S.COLNAME = T.COLNAME AND S.COLTYPE = T.COLTYPE AND S.COLLEN = T.COLLEN '
                + ' WHERE S.COLNAME IS NULL OR T.COLNAME IS NULL '
          --4.1.3 执行动态脚本
          --PRINT @SSQL
          --PRINT '================'
          EXECUTE(@SSQL)
          
          --4.2 处理下一条
          FETCH DBS INTO @DBNAME
        END
        
        
        --5. 查询结果集(可以直接在这里生成同步脚本)
        SELECT * FROM #TMP_COLUMN_DIFF
        SELECT * FROM #TMP_TABLE_DIFF
        SELECT * FROM #TMP_DB_DIFF
        
        --6. 删除临时表
        DROP TABLE #TMP_COLUMN_DIFF
        DROP TABLE #TMP_TABLE_DIFF
        DROP TABLE #TMP_DB_DIFF
        
        --7. 关闭游标
        CLOSE DBS
        --8. 销毁游标
        DEALLOCATE DBS
        
        END TRY
        BEGIN CATCH
            --关闭游标
            CLOSE DBS
            --销毁游标
            DEALLOCATE DBS
            --打印错误信息
            PRINT ERROR_NUMBER()
            PRINT ERROR_MESSAGE()
        END CATCH
        
END

二、执行过程

EXEC PR_DB_STRUCT_COMPARE 'DB_', 'dev3310Link'  -- 过程的参数请参阅脚本中的说明
三、查看结果
执行完毕后返回的结果集就是我们想要的结果。在需要时也可以修改存储过程中的临时表为实体表,根据需求调整即可


猜你喜欢

转载自blog.csdn.net/leandzgc/article/details/80004650
今日推荐