SQL Server:如何比较存储在变量中的两个查询的结果

时间:2016-05-12 12:56:57

标签: sql-server

我正在尝试比较存储在变量中的查询的两个结果集。

我尝试了以下内容:

DECLARE @sql1 varchar(8000) = 'SELECT * FROM table1'
DECLARE @sql2 varchar(8000) = 'SELECT Col2, Col1 FROM table1'

IF EXISTS(
 (EXEC sp_executesql @sql1 
   EXCEPT 
  EXEC sp_executesql @sql2) 
    UNION ALL
 (EXEC sp_executesql @sql2
   EXCEPT 
  EXEC sp_executesql @sql1))

这种方法有两个问题:if语句不喜欢EXEC语句(当您使用实际查询而不是变量时,EXCEPT UNION ALL EXCEPT结构有效)。

第二个问题是,即使您使用实际查询,两个查询的列的顺序也必须相同或者结果集不匹配。但是,出于我的目的,我需要匹配这些结果集。我想我需要一种方法来订购列,但我不确定这是否可能。

编辑: 我无法控制传入的查询,因为这是一个应用程序的代码,用于根据教师的查询检查学生的答案查询。我不能选择不使用*。

1 个答案:

答案 0 :(得分:0)

您尝试实现的目标并非在SQL Server中完成。一种相当干净的方法是使用代码执行每个查询,并按元数据和值逐个值比较结果数据集。

我不建议您使用以下方法。

出于测试目的,我创建了这个存储过程:

IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'SP_CompareQueryResults')
    DROP PROCEDURE SP_CompareQueryResults
GO

CREATE PROCEDURE SP_CompareQueryResults 
(
    @sql1 NVARCHAR(4000)
  , @sql2 NVARCHAR(4000)
)
AS
BEGIN

DECLARE @q1 NVARCHAR(MAX) = @sql1
, @q2 NVARCHAR(MAX) = @sql2

IF OBJECT_ID('tempdb..##q1') IS NOT NULL
    DROP TABLE ##q1
IF OBJECT_ID('tempdb..##q2') IS NOT NULL
    DROP TABLE ##q2

SET @q1 = 'SELECT * INTO ##q1 FROM (' + @q1 + ') r'
SET @q2 = 'SELECT * INTO ##q2 FROM (' + @q2 + ') r'

BEGIN TRY
    EXEC (@q1)
    EXEC (@q2)
END TRY
BEGIN CATCH
    SELECT 'One of the source queries are not valid.'
    RETURN
END CATCH

DECLARE @r NVARCHAR(MAX)

SELECT @r = COALESCE(@r + ', ', ' ') + COLUMN_NAME
FROM (
    SELECT COLUMN_NAME
    FROM tempdb.INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = '##q1' 
    INTERSECT
    SELECT COLUMN_NAME
    FROM tempdb.INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = '##q2'
) r

SET @r = 'SELECT 1 as SourceQuery, * FROM (SELECT ' + @r + ' FROM ##q1 EXCEPT SELECT' + @r + ' FROM ##q2) r'
+ ' UNION ALL SELECT 2 as SourceQuery, * FROM (SELECT ' + @r + ' FROM ##q2 EXCEPT SELECT' + @r + ' FROM ##q1) r'

BEGIN TRY
    EXEC(@r)    
END TRY
BEGIN CATCH
    SELECT 'Queries have not matching metadata.'
    RETURN
END CATCH

IF OBJECT_ID('tempdb..##q1') IS NOT NULL
    DROP TABLE ##q1
IF OBJECT_ID('tempdb..##q2') IS NOT NULL
    DROP TABLE ##q2
END
GO

它从两个查询中找到具有相同名称的列,并比较每个查询结果,返回查询1中未包含在查询2中的行,反之亦然。

假设您有两个查询,结果如下:

sql query one

另一个:

sql query two

正如您所看到的,第二个查询有一个额外的列,列的顺序不同,两个查询中只有一个元组。

执行上述SP:

EXEC SP_CompareQueryResults
@sql1 = N'
SELECT 1 AS ID
, ''test'' AS Value
, CAST(1 as BIT) AS Valid
UNION ALL 
SELECT 2, ''test2'', 0',
@sql2 = N'
SELECT 1 AS ID
, CAST(1 as BIT) AS Valid
, ''test'' AS Value
, ''test'' AS AnotherValue
UNION ALL 
SELECT 2, 1, ''test2'', ''whatever'''

为您提供两个查询中无匹配的元组:

results

如果两个查询产生相同的结果,则SP_CompareQueryResults不会返回任何行,因此您可以说它们具有匹配名称的列具有相同的值。如果两个查询都没有结果行,则会发生同样的事情,从而产生误报。您可以根据需要调整上述程序。

请勿在生产环境中使用此代码,因为它可能是SQL注入的,我还没有对此进行测试。一般情况下,如果没有必要,请避免使用动态sql。

如上所述,尝试编写例如c#代码是sql injection safe并比较结果。