Question

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
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top