54

我正在稍微修改一个 sql server 2005 存储过程以提高性能,我想快速确保旧存储过程和新存储过程返回完全相同的结果(列相同,我想确保行是相同的)。

在 sql server 2005 中是否有一种简单的方法可以做到这一点?

4

7 回答 7

70

您可以使用 except 构造在两个查询之间进行匹配。

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

编辑:

然后逆向查询,找出以query2为驱动的差异:

select * from (select * from query2) as query2
except
select * from (select * from query1) as query1
于 2012-06-13T15:00:00.333 回答
4

下面的存储过程将比较 2 个存储过程或 2 个语句的输出结果集。这里的关键是SP不需要知道结果集的结构或模式,因此您可以任意测试任何SP。如果输出相同,它将返回 0 行。此解决方案在 SQL Server 中使用 openrowset 命令。这是存储过程的一些示例用法

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

SP 需要以下先决条件,这对于生产环境可能并不理想,但对于本地 QA、DEV 和测试环境非常有用。它在代码中使用 openrowset。

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

这是存储过程的代码。

==================================================================================
    --== 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
于 2017-11-15T20:31:45.563 回答
4

要完成@jabs的回答,您可以使用以下模板来获取两个查询之间的区别:

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);

示例 1:这将返回 0 行,因为查询是相同的

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);

示例 2:这将返回查询之间的不同行(其中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);

示例 3:为了好玩,您可以检查示例 2 中的查询是否与查询 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);
于 2020-08-12T16:16:39.280 回答
3
create table #OldProcResults (
    <Blah>
)

create table #NewProcResults (
    <Blih>
)

insert into #OldProcResults
    exec MyOldProc

insert into #NewProcResults
    exec MyNewProc

然后使用 Jabs 的答案来比较这两个表。

于 2012-06-13T15:06:19.307 回答
2

EXCEPT 是比较两个查询的关键(正如@jabs 所说)。

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

为每个查询添加count(*)以确保两者具有相同的结果。以防万一有一些重复的行被除外删除。

于 2019-04-26T09:06:35.873 回答
0

创建两个临时表,每个过程一个。运行该过程以将行插入到相应的表中。

然后从一个中选择 * 减去从另一个中选择 * 反之亦然

于 2012-06-13T14:59:41.707 回答
0

这里有一些更详细的方法可以帮助我在 MSSQL 上研究这个问题。@tpvasconcelos 的答案是迄今为止最正确的。

DECLARE @AAA TABLE(id bigint NOT NULL) 
    INSERT INTO @AAA
    VALUES (1),(2),(3),(4),(5),(6),(7)

DECLARE @bbb TABLE(id bigint NOT NULL)
    INSERT INTO @bbb
    VALUES (1),(2),(3),(4),(5),(6),(7)

Declare @diff int = (SELECT COUNT(*) FROM (SELECT * FROM @AAA EXCEPT SELECT * FROM @bbb) AS TB)


Declare @aux1 int;
set @aux1 = (select count(*) from @BBB);

Declare @aux2 int;
set @aux2 = (SELECT COUNT(*)  FROM @AAA)

Declare @aux3 int;
set @aux3 = (SELECT COUNT(*) FROM (select * from @AAA union SELECT * FROM @bbb) as tb);  -- for union to work it needs a alias


IF @diff <> 0
    begin
        PRINT 'Flow @flows_name has failed.'
    end
else
    begin
        IF @aux1 = @aux3
            begin
                PRINT 'Flow @flows_name might have SUCCEEDED!'
            end
        else
            begin
                PRINT 'Flow @flows_name has failed.'
            end
    end

于 2021-09-26T18:32:16.867 回答