SQL Server compare results of two queries that should be identical

前端 未结 6 1999
面向向阳花
面向向阳花 2020-12-24 11:14

I am modifying a sql server 2005 stored procedure slightly for performance, and I would like to quickly make sure the old stored proc and the new one return the exact same r

相关标签:
6条回答
  • 2020-12-24 11:28
    create table #OldProcResults (
        <Blah>
    )
    
    create table #NewProcResults (
        <Blih>
    )
    
    insert into #OldProcResults
        exec MyOldProc
    
    insert into #NewProcResults
        exec MyNewProc
    

    then use Jabs' answer to compare the two tables.

    0 讨论(0)
  • 2020-12-24 11:37

    The stored proc below will compare the output resultset of 2 stored procedures, or 2 statements. The key here is the SP does not need to know the structure or schema of the result set, thus you can arbitrarily test any SP. It will return 0 rows if the output is the same. This solution uses openrowset command in SQL Server. Here is some sample usage of the Stored proc

    DECLARE @SQL_SP1 VARCHAR(MAX)
    DECLARE @SQL_SP2 VARCHAR(MAX)
    
    -- Compare results of 2 Stored Procs
    SET @SQL_SP1 = 'EXEC SomeDB.dbo.[usp_GetWithTheProgram_OLD] 100, ''SomeParamX'''
    SET @SQL_SP1 = 'EXEC SomeDB.dbo.[usp_GetWithTheProgram_NEW] 50, ''SomeParamX'''
    EXEC utlCompareStatementResults @SQL_SP1, @SQL_SP2
    
    -- Compare just 2 SQL Statements
    SET @SQL_SP1 = 'SELECT * FROM SomeDB.dbo.Table1 WHERE CreatedOn > ''2016-05-08'''
    SET @SQL_SP1 = 'SELECT * FROM SomeDB.dbo.Table1 WHERE CreatedOn > ''2016-06-11'''
    EXEC utlCompareStatementResults @SQL_SP1, @SQL_SP2
    

    The SP requires the following prerequisites, which may not be ideal for a production environment, but very useful for local QA, DEV and Test environments. It uses openrowset in the code.

    EXEC sp_configure 'show advanced options', 1
    EXEC sp_configure 'ad hoc distributed queries', 1
    EXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUE
    

    Here is the code for the stored proc.

    ==================================================================================
        --== SUMMARY utlCompareStatementResults
        --==    - requires sp_configure 'show advanced options', 1
        --==    - requires sp_configure 'ad hoc distributed queries', 1
        --==    - maybe requires EXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUE
        --==    - requires the RecordSet Output to have Unique ColumnNames (no duplicate columns)
        --==    - requires references in straight SQL to be fully qualified [dbname].[schema].[objects] but not within an SP
        --==    - requires references SP call to be fully qualifed [dbname].[schema].[spname] but not objects with the SP
        --== OUTPUT
        --==    Differences are returned 
        --==    If there is no recordset returned, then theres no differences
        --==    However if you are comparing 2 empty recordsets, it doesn't mean anything
        --== USAGE
        --==   DECLARE @SQL_SP1 VARCHAR(MAX)
        --==   DECLARE @SQL_SP2 VARCHAR(MAX)
        --==   -- Compare just 2 SQL Statements
        --==   SET @SQL_SP1 = 'SELECT * FROM SomeDB.dbo.Table1 WHERE CreatedOn > ''2016-05-08'''
        --==   SET @SQL_SP1 = 'SELECT * FROM SomeDB.dbo.Table1 WHERE CreatedOn > ''2016-06-11'''
        --==   EXEC utlCompareStatementResults @SQL_SP1, @SQL_SP2
        --==
        --==   -- Compare results of 2 Stored Procs
        --==   SET @SQL_SP1 = 'EXEC SomeDB.dbo.[usp_GetWithTheProgram_OLD] 100, ''SomeParamX'''
        --==   SET @SQL_SP1 = 'EXEC SomeDB.dbo.[usp_GetWithTheProgram_NEW] 50, ''SomeParamX'''
        --==   EXEC utlCompareStatementResults @SQL_SP1, @SQL_SP2
        --==================================================================================
        CREATE PROCEDURE utlCompareStatementResults
           @SQL_SP1 VARCHAR(MAX),
           @SQL_SP2 VARCHAR(MAX)
        AS
        BEGIN
            DECLARE @TABLE1 VARCHAR(200)
            DECLARE @TABLE2 VARCHAR(200)
            DECLARE @SQL_OPENROWSET VARCHAR(MAX) 
            DECLARE @CONNECTION VARCHAR(100)
    
            SET @CONNECTION = 'server='+@@SERVERNAME+';Trusted_Connection=yes'
    
            SET @SQL_SP1 = REPLACE(@SQL_SP1, '''','''''')
            SET @SQL_SP2 = REPLACE(@SQL_SP2, '''','''''')
    
            SET @TABLE1 = '#' + SUBSTRING(CONVERT(VARCHAR(250),NEWID()), 1, 8)
            SET @TABLE2 = '#' + SUBSTRING(CONVERT(VARCHAR(250),NEWID()), 1, 8)
    
            SET @SQL_OPENROWSET =
            'SELECT * ' + ' ' +
            'INTO ' + @TABLE1 + ' ' +
            'FROM OPENROWSET(''SQLNCLI'', ' + '''' + @CONNECTION + '''' +
                            ',''' + @SQL_SP1 +'''); ' +
            'SELECT * ' + ' ' +
            'INTO ' + @TABLE2 + ' ' +
            'FROM OPENROWSET(''SQLNCLI'', ' + '''' + @CONNECTION + '''' +
                            ',''' + @SQL_SP2 +'''); ' +
            '(SELECT * FROM ' + @TABLE1 + ' EXCEPT SELECT * FROM ' + @TABLE2 + ') '  +
            ' UNION ALL ' +
            '(SELECT * FROM ' + @TABLE2 + ' EXCEPT SELECT * FROM ' + @TABLE1 + '); ' +
            'DROP TABLE ' + @TABLE1 + '; ' +
            'DROP TABLE ' + @TABLE2 + '; '
            PRINT @SQL_OPENROWSET
            EXEC (@SQL_OPENROWSET)
            PRINT 'DifferenceCount: ' + CONVERT(VARCHAR(100), @@ROWCOUNT)
        END
    
    0 讨论(0)
  • 2020-12-24 11:44

    you can use the except construct to match between the two queries.

    select * from (select * from query1) as query1
    except
    select * from (select * from query2) as query2
    

    EDIT:

    Then reverse the query to find differences with query2 as the driver:

    select * from (select * from query2) as query2
    except
    select * from (select * from query1) as query1
    
    0 讨论(0)
  • 2020-12-24 11:46

    To complete @jabs answer, you can use the following template to get the difference between two queries:

    WITH q1 AS (<INSERT_QUERY_1_HERE>)
       , q2 AS (<INSERT_QUERY_2_HERE>)
    SELECT * FROM q1 EXCEPT SELECT * FROM q2
    UNION ALL (
    SELECT * FROM q2 EXCEPT SELECT * FROM q1);
    

    Example 1: This returns 0 rows, as the queries are identical

    WITH q1 AS (SELECT * FROM my_table)
       , q2 AS (SELECT * FROM my_table)
    SELECT * FROM q1 EXCEPT SELECT * FROM q2
    UNION ALL (
    SELECT * FROM q2 EXCEPT SELECT * FROM q1);
    

    Example 2: This returns the different rows between the queries (where foo = 'bar')

    WITH q1 AS (SELECT * FROM my_table)
       , q2 AS (SELECT * FROM my_table WHERE foo <> 'bar')
    SELECT * FROM q1 EXCEPT SELECT * FROM q2
    UNION ALL (
    SELECT * FROM q2 EXCEPT SELECT * FROM q1);
    

    Example 3: Just for fun, you can check that the query in Example 2 is identical to queries the rows where foo = 'bar'.

    WITH q1 AS (
    
        WITH q1 AS (SELECT * FROM my_table)
           , q2 AS (SELECT * FROM my_table WHERE foo <> 'bar')
        SELECT * FROM q1 EXCEPT SELECT * FROM q2
        UNION ALL (
        SELECT * FROM q2 EXCEPT SELECT * FROM q1)
    
    )
       , q2 AS (SELECT * FROM my_table WHERE foo = 'bar')
    SELECT * FROM q1 EXCEPT SELECT * FROM q2
    UNION ALL (
    SELECT * FROM q2 EXCEPT SELECT * FROM q1);
    
    0 讨论(0)
  • 2020-12-24 11:46

    EXCEPT is the key to compare two querys (as @jabs said).

    SELECT count(*), * FROM "query 1 here"
    EXCEPT
    SELECT count(*), * FROM "query 2 here"
    

    Adding count(*) for each query to make sure both have the same results. Just in case there are some repeated rows which are deleted by except.

    0 讨论(0)
  • 2020-12-24 11:52

    create two temp tables, one for each procedure. run the procedure to insert rows into the appropriate table.

    then select * from one MINUS select * from the other and visa-versa

    0 讨论(0)
提交回复
热议问题