比较本地和远程DB上的列名称

时间:2014-02-11 18:44:04

标签: mysql sql

我有一个引用远程数据库的定时同步。同步使用“选择* ...”。本地数据库结构发生了一些变化,现在同步失败,因为列不同。

如何比较两张表?

要获得当地的校友,我可以这样做:

SELECT * -- COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
FROM   information_schema.columns
WHERE  table_name = 'Items'
ORDER  BY ordinal_position  

要获得我可以做的远程列:

EXECUTE [WebServ].[WebDB].dbo.sp_executesql
N'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM
INFORMATION_SCHEMA.COLUMNS
WHERE  table_name like ''ITEMS_Web''' 

但是我怎么把它们放在一起所以它只显示我在本地表上添加了哪些列?我想我只是不知道如何将Execute命令的结果变成一个可用的形式,所以我可以做一个JOIN或者什么......原谅我的n00bieness。这是MSSQL 2008

1 个答案:

答案 0 :(得分:1)

我有一个存储过程来自动修改存档表。

我已经为你修改了它,保留@ COMMIT_CHANGES = 0并打印出更新第二个表以匹配第一个表所需的更改。

确保在SET语句中修复数据库和所有者以匹配您自己的数据库表。

我已经禁用了可能会改变你的表的EXEC,以防你不小心将@COMMIT_CHANGES设置为1 ...如果你想让@COMMIT_CHANGES标志实际执行alter代码,请取消注释。如果没有返回任何表,则表匹配。

编辑:我正在将表中的命令存储到自己的电子邮件中,但我从此示例中删除了电子邮件代码。

这应该为你需要它做的任何事情奠定良好的基础。

DECLARE @TABLENAME VARCHAR(100), @TABLENAME2 VARCHAR(100), @COMMIT_CHANGES BIT, @SSERVERNAME VARCHAR(100)

SET @TABLENAME='database.dbo.ITEMS' --local
SET @TABLENAME2='database.dbo.ITEMS_WEB' --remote
SET @SSERVERNAME = 'WEBSERV' --remote server
SET @COMMIT_CHANGES=0 -- set to 1, and it WILL change your remote table

    SET NOCOUNT ON

    DECLARE @SQL VARCHAR(MAX)
    DECLARE @DB1 SYSNAME, @OWNER1 SYSNAME, @TABLE1 SYSNAME
    DECLARE @DB2 SYSNAME, @OWNER2 SYSNAME, @TABLE2 SYSNAME
    DECLARE @RECIPIENTS VARCHAR(500), @ENABLEEMAIL BIT

    IF @COMMIT_CHANGES = 0 PRINT 'TEST MODE ONLY, CHANGES WILL NOT BE MADE'

    -- PARSE TABLE NAME INTO 3 PARTS
    SELECT @TABLE1 = PARSENAME(@TABLENAME, 1)
    SELECT @OWNER1 = PARSENAME(@TABLENAME, 2)
    IF @OWNER1 IS NULL SELECT @OWNER1 = 'DBO'
    SELECT @DB1 = PARSENAME(@TABLENAME, 3) 
    IF @DB1 IS NULL SELECT @DB1 = DB_NAME()

    -- PARSE ARCHIVE TABLE NAME INTO 3 PARTS
    SELECT @TABLE2 = PARSENAME(@TABLENAME2, 1)
    SELECT @OWNER2 = PARSENAME(@TABLENAME2, 2)
    IF @OWNER2 IS NULL SELECT @OWNER2 = 'DBO'
    SELECT @DB2 = PARSENAME(@TABLENAME2, 3) 
    IF @DB2 IS NULL SELECT @DB2 = DB_NAME()

    -- IF OUR TEMP TABLES EXIST, DROP THEM
    IF EXISTS (SELECT * FROM TEMPDB.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '##T1_MAIN') DROP TABLE ##T1_MAIN
    IF EXISTS (SELECT * FROM TEMPDB.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '##T2_ARCHIVE') DROP TABLE ##T2_ARCHIVE

    -- GATHER SCHEMA INFO FOR LIVE TABLE
    SET @SQL = 'SELECT TABLE_NAME, 
                COLUMN_NAME, 
                DATA_TYPE, 
                ISNULL(CHARACTER_MAXIMUM_LENGTH,0) AS CHARACTER_MAXIMUM_LENGTH,
                ISNULL(NUMERIC_PRECISION,0) AS NUMERIC_PRECISION,
                ISNULL(NUMERIC_SCALE,0) AS NUMERIC_SCALE,
                IS_NULLABLE,
                CAST(0 AS BIT) AS ADD_COLUMN,
                CAST(0 AS BIT) AS ALTER_COLUMN
                INTO ##T1_MAIN
                FROM ' + @DB1 + '.INFORMATION_SCHEMA.COLUMNS 
                WHERE TABLE_NAME = ''' + @TABLE1 + '''
                AND TABLE_SCHEMA = ''' + @OWNER1 + ''' '
    --PRINT @SQL
    EXEC(@SQL)

    -- CHECK IF TABLES EXIST, ELSE SKIP ALL WORK
    IF NOT EXISTS (SELECT * FROM TEMPDB.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '##T1_MAIN')
    BEGIN
        PRINT @TABLENAME + ' DOES NOT EXIST, EXITING PROC'
        GOTO SKIPWORK
    END

    -- GATHER SCHEMA INFO FOR ARCHIVE TABLE
    SET @SQL = 'SELECT TABLE_NAME, 
                COLUMN_NAME, 
                DATA_TYPE, 
                ISNULL(CHARACTER_MAXIMUM_LENGTH,0) AS CHARACTER_MAXIMUM_LENGTH,
                ISNULL(NUMERIC_PRECISION,0) AS NUMERIC_PRECISION,
                ISNULL(NUMERIC_SCALE,0) AS NUMERIC_SCALE,  
                IS_NULLABLE,         
                CAST(0 AS BIT) AS DROP_COLUMN
                INTO ##T2_ARCHIVE
                FROM ['+@SSERVERNAME+'].' + @DB2 + '.INFORMATION_SCHEMA.COLUMNS 
                WHERE TABLE_NAME = ''' + @TABLE2 + ''' 
                AND TABLE_SCHEMA = ''' + @OWNER2 + ''' '
    --PRINT @SQL
    EXEC(@SQL)

    -- CHECK IF TABLES EXIST, ELSE SKIP ALL WORK
    IF NOT EXISTS (SELECT * FROM TEMPDB.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '##T2_ARCHIVE')
    BEGIN
        PRINT @TABLENAME2 + ' DOES NOT EXIST, EXITING PROC'
        GOTO SKIPWORK
    END

    -- FLAG NEW COLUMNS
    -- COLUMN IN T1 (LIVE) BUT NOT IN T2 (ARCHIVE)
    UPDATE T1 SET ADD_COLUMN = 1
    FROM ##T1_MAIN T1
    LEFT OUTER JOIN ##T2_ARCHIVE T2
       ON T1.COLUMN_NAME = T2.COLUMN_NAME
    WHERE T2.COLUMN_NAME IS NULL

    -- FLAG REMOVED COLUMNS
    -- COLUMN IN T2 (ARCHIVE) BUT NOT IN T1 (LIVE)
    -- ** NOT DOING ANYTHING WITH THIS **
    UPDATE T2 SET DROP_COLUMN = 1
    FROM ##T2_ARCHIVE T2
    LEFT OUTER JOIN ##T1_MAIN T1
       ON T2.COLUMN_NAME = T1.COLUMN_NAME
    WHERE T1.COLUMN_NAME IS NULL

    -- FLAG ALTERED COLUMNS     
    -- ONLY NEED WHERE LIVE DATA LENGTH IS > THAN ARCHIVE DATA LENGTH
    -- WE WOULDN'T WANT TO SHRINK A COLUMN AND TRUNCATE A VALUE           
    UPDATE T1 SET ALTER_COLUMN = 1
    FROM ##T1_MAIN T1
    JOIN ##T2_ARCHIVE T2
       ON T1.COLUMN_NAME = T2.COLUMN_NAME
       AND (T1.DATA_TYPE <> T2.DATA_TYPE
            OR T1.CHARACTER_MAXIMUM_LENGTH > T2.CHARACTER_MAXIMUM_LENGTH
            OR T1.NUMERIC_PRECISION > T2.NUMERIC_PRECISION
            OR T1.NUMERIC_SCALE > T2.NUMERIC_SCALE
            OR (T1.IS_NULLABLE = 'YES' AND T2.IS_NULLABLE = 'NO'))

    DECLARE @COLUMN_NAME VARCHAR(100),
            @DATA_TYPE VARCHAR(100),
            @CHARACTER_MAXIMUM_LENGTH INT,
            @NUMERIC_PRECISION INT,
            @NUMERIC_SCALE INT,
            @IS_NULLABLE VARCHAR(3)

    -- CREATE A TEMP TABLE TO HOLD OUR COMMANDS FOR EMAIL
    IF EXISTS (SELECT * FROM TEMPDB.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '##COMMANDLIST') DROP TABLE ##COMMANDLIST

    CREATE TABLE ##COMMANDLIST (sText VARCHAR(1000))

    DECLARE ALTER_COLUMN CURSOR LOCAL
    FOR
       SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE, IS_NULLABLE
       FROM ##T1_MAIN
       WHERE ALTER_COLUMN=1

       OPEN ALTER_COLUMN

       FETCH NEXT FROM ALTER_COLUMN INTO @COLUMN_NAME, @DATA_TYPE, @CHARACTER_MAXIMUM_LENGTH, @NUMERIC_PRECISION, @NUMERIC_SCALE, @IS_NULLABLE

       WHILE @@FETCH_STATUS = 0
       BEGIN
          SET @SQL = 'ALTER TABLE ' + @DB2 + '.' + @OWNER2 + '.' + @TABLE2 + ' ALTER COLUMN ' + @COLUMN_NAME + ' ' + CASE 
                                                                                                                        WHEN @DATA_TYPE IN ('NCHAR','NVARCHAR','CHAR','VARCHAR') THEN @DATA_TYPE + ' (' + CAST(@CHARACTER_MAXIMUM_LENGTH AS VARCHAR(15)) + ')'
                                                                                                                        WHEN @DATA_TYPE IN ('TINYINT','SMALLINT','INT','BIGINT','BIT','UNIQUEIDENTIFIER') THEN @DATA_TYPE
                                                                                                                        WHEN @DATA_TYPE IN ('DECIMAL','MONEY','FLOAT','NUMERIC') THEN @DATA_TYPE + ' (' + CAST(@NUMERIC_PRECISION AS VARCHAR(15)) + ',' + CAST(@NUMERIC_SCALE AS VARCHAR(15)) + ')'
                                                                                                                     END
                                                                                                                     + ' ' +
                                                                                                                     CASE
                                                                                                                        WHEN @IS_NULLABLE = 'YES' THEN 'NULL'
                                                                                                                        ELSE 'NULL'
                                                                                                                     END
          PRINT @SQL
          IF @COMMIT_CHANGES = 1 
          BEGIN
             IF(@@SERVERNAME <> @sServerName) SET @SQL = 'EXEC(''' + REPLACE(@SQL,'''','''''') + ''') AT ' + QUOTENAME(@sServerName)   
             EXEC(@SQL)
          END

          IF @SQL IS NOT NULL INSERT INTO ##COMMANDLIST (sText) SELECT @SQL

          FETCH NEXT FROM ALTER_COLUMN INTO @COLUMN_NAME, @DATA_TYPE, @CHARACTER_MAXIMUM_LENGTH, @NUMERIC_PRECISION, @NUMERIC_SCALE, @IS_NULLABLE
       END
       CLOSE ALTER_COLUMN
    DEALLOCATE ALTER_COLUMN

    DECLARE ADD_COLUMN CURSOR LOCAL
    FOR
       SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE, IS_NULLABLE
       FROM ##T1_MAIN
       WHERE ADD_COLUMN=1

       OPEN ADD_COLUMN

       FETCH NEXT FROM ADD_COLUMN INTO @COLUMN_NAME, @DATA_TYPE, @CHARACTER_MAXIMUM_LENGTH, @NUMERIC_PRECISION, @NUMERIC_SCALE, @IS_NULLABLE

       WHILE @@FETCH_STATUS = 0
       BEGIN
          SET @SQL = 'ALTER TABLE ' + @DB2 + '.' + @OWNER2 + '.' + @TABLE2 + ' ADD ' + @COLUMN_NAME + ' ' + CASE 
                                                                                                               WHEN @DATA_TYPE IN ('NCHAR','NVARCHAR','CHAR','VARCHAR') THEN @DATA_TYPE + ' (' + CAST(@CHARACTER_MAXIMUM_LENGTH AS VARCHAR(15)) + ')'
                                                                                                               WHEN @DATA_TYPE IN ('TINYINT','SMALLINT','INT','BIGINT','BIT','UNIQUEIDENTIFIER') THEN @DATA_TYPE
                                                                                                               WHEN @DATA_TYPE IN ('DECIMAL','MONEY','FLOAT','NUMERIC') THEN @DATA_TYPE + ' (' + CAST(@NUMERIC_PRECISION AS VARCHAR(15)) + ',' + CAST(@NUMERIC_SCALE AS VARCHAR(15)) + ')'
                                                                                                            END
                                                                                                            + ' ' +
                                                                                                            CASE
                                                                                                               WHEN @IS_NULLABLE = 'YES' THEN 'NULL'
                                                                                                               ELSE 'NOT NULL'
                                                                                                            END
          PRINT @SQL
          IF @COMMIT_CHANGES = 1 
          BEGIN
             IF(@@SERVERNAME <> @sServerName) SET @SQL = 'EXEC(''' + REPLACE(@SQL,'''','''''') + ''') AT ' + QUOTENAME(@sServerName)   
             -- uncomment this EXEC to make the commit changes flag work...
             --EXEC(@SQL)
          END

          IF @SQL IS NOT NULL INSERT INTO ##COMMANDLIST (sText) SELECT @SQL

          FETCH NEXT FROM ADD_COLUMN INTO @COLUMN_NAME, @DATA_TYPE, @CHARACTER_MAXIMUM_LENGTH, @NUMERIC_PRECISION, @NUMERIC_SCALE, @IS_NULLABLE
       END
       CLOSE ADD_COLUMN
    DEALLOCATE ADD_COLUMN

    SKIPWORK: