我有一个引用远程数据库的定时同步。同步使用“选择* ...”。本地数据库结构发生了一些变化,现在同步失败,因为列不同。
如何比较两张表?
要获得当地的校友,我可以这样做:
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
答案 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: