I'm trying to compare some tables in a pair of databases using EXCEPT. The script should output the number of rows that are different between the two tables. The script below will retrieve the tables and columns that I want to include, but I'm wondering if there is any way to build my query without using dynamic sql?
I have no problem using dynamic sql. I'm just curious to know if there's a better way. TIA.
DECLARE @tbl sysname, @col sysname
DECLARE tblCursor CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
ORDER BY TABLE_NAME
OPEN tblCursor
FETCH NEXT FROM tblCursor INTO @tbl
WHILE (@@FETCH_STATUS = 0)
BEGIN
DECLARE colCursor CURSOR FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME=@tbl AND COLUMN_NAME <> 'PR'
OPEN colCursor
FETCH NEXT FROM colCursor INTO @col
WHILE (@@FETCH_STATUS = 0)
BEGIN
-- Do I have to build dynamic SQL string for
-- columns here or is there a better way?
FETCH NEXT FROM colCursor INTO @col
END
CLOSE colCursor
DEALLOCATE colCursor
/* Use EXCEPT to compare two tables
DECLARE @tblDeviationCount int = (
SELECT COUNT(*)
FROM (
SELECT [Column1], [Column3]
FROM @tbl
EXCEPT (
SELECT [Column1], [Column3]
FROM @tblOther
)
UNION
SELECT [Column1], [Column3]
FROM @tblOther
EXCEPT (
SELECT [Column1], [Column3]
FROM @tbl
)
) subq
)
*/
PRINT @tbl + ': ' + @tblDeviationCount + ' deviations found.'
FETCH NEXT FROM tblCursor INTO @tbl
END
CLOSE tblCursor
DEALLOCATE tblCursor
There is no way to replace object names with variables in a query without using dynamic sql, so you are heading down the right path. As an aside though you can remove your column cursor, and you can get a full column list using SQL Server's XML extensions to concatenate the columns:
DECLARE @Cols NVARCHAR(MAX) = STUFF((SELECT ',' + c.Name
FROM sys.columns c
WHERE c.object_id = OBJECT_ID(@tbl)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
This won't make much difference at all, but every little helps, and avoids allocating memory to a cursor. It does also make it easier to get the columns common to both tables as you can use an INNER JOIN, or INTERSECT to get the relevant columns.
The other change I would make (although this is much more subjective) is that I would use the system views for your schema
information, rather than the INFORMATION_SCHEMA
, the information schema, although ANSI standard and more guaranteed does
have its flaws, and the system views are more accurate and contain more information. Aaron Bertrand makes a much better case than me in this article.