我正在尝试比较存储在变量中的查询的两个结果集。
我尝试了以下内容:
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结构有效)。
第二个问题是,即使您使用实际查询,两个查询的列的顺序也必须相同或者结果集不匹配。但是,出于我的目的,我需要匹配这些结果集。我想我需要一种方法来订购列,但我不确定这是否可能。
编辑: 我无法控制传入的查询,因为这是一个应用程序的代码,用于根据教师的查询检查学生的答案查询。我不能选择不使用*。
答案 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中的行,反之亦然。
假设您有两个查询,结果如下:
另一个:
正如您所看到的,第二个查询有一个额外的列,列的顺序不同,两个查询中只有一个元组。
执行上述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'''
为您提供两个查询中无匹配的元组:
如果两个查询产生相同的结果,则SP_CompareQueryResults不会返回任何行,因此您可以说它们具有匹配名称的列具有相同的值。如果两个查询都没有结果行,则会发生同样的事情,从而产生误报。您可以根据需要调整上述程序。
请勿在生产环境中使用此代码,因为它可能是SQL注入的,我还没有对此进行测试。一般情况下,如果没有必要,请避免使用动态sql。
如上所述,尝试编写例如c#代码是sql injection safe并比较结果。