证明SQL查询的等效性

时间:2020-03-05 18:51:35  来源:igfitidea点击:

我们将如何证明两个查询在功能上是等效的,例如它们将始终都返回相同的结果集。

由于在执行此操作时会想到一个特定的查询,因此最终按照@dougman的建议进行操作,在有关表的约10%的行中进行比较并比较结果,以确保没有不合适的结果。

解决方案

回答

在我看来,这听起来像是一个NP完全问题。我不确定是否有肯定的方法可以证明这种事情

回答

DBMS供应商已经为此工作了很长时间。正如Rik所说,这可能是一个棘手的问题,但我认为尚未对问题空间的NP完整性进行任何正式的分析。

但是,最好的选择是尽可能利用DBMS。所有DBMS系统都将SQL转换为某种查询计划。我们可以使用此查询计划(它是查询的抽象版本)作为一个很好的起点(DBMS会进行很多优化,将查询平整为更可行的模型)。

注意:现代的DBMS使用"基于成本"的分析器,该分析器在统计信息更新之间不确定,因此查询计划者可能会随着时间的流逝而更改相同查询的查询计划。

在Oracle中(取决于版本),我们可以通过SQL提示告诉优化器从基于成本的分析器切换到基于确定性规则的分析器(这将简化计划分析)。

SELECT /*+RULE*/ FROM yourtable

自8i以来,基于规则的优化器已被弃用,但它甚至可以挂在10g左右(我不知道"回合11")。但是,基于规则的分析器要复杂得多:错误率可能更高。

为了进一步了解更一般的性质,IBM拥有其查询优化专利的多产。这是将SQL转换为"抽象计划"的方法的一个很好的起点:
http://www.patentstorm.us/patents/7333981.html

回答

你不知道

例如,如果我们需要高度的信心来确定性能变化并没有改变查询的输出,那么请进行全面测试。

如果我们需要非常高的信心..那么,errrm,请对其进行更多测试。

对于SQL查询,很难进行大规模的测试。编写一个proc,该proc会围绕一大组/完整的可能的参数进行迭代,并使用每组参数调用每个查询,并将输出写入相应的表中。比较两个表,就可以了。

这不是完全科学,我猜这是OP的问题,但是我不知道证明等效性的正式方法。

回答

最好的办法是根据给定的一组输入比较两个查询输出,以查找任何差异。要说它们对于所有输入将始终返回相同的结果,实际上取决于数据。

对于Oracle,最好的方法之一(如果不是最好的话)(非常有效)在这里(Ctrl + F比较两个表的内容):
http://www.oracle.com/technetwork/issue-archive/2005/05-jan/o15asktom-084959.html

归结为:

select c1,c2,c3, 
       count(src1) CNT1, 
       count(src2) CNT2
  from (select a.*, 
               1 src1, 
               to_number(null) src2 
          from a
        union all
        select b.*, 
               to_number(null) src1, 
               2 src2 
          from b
       )
group by c1,c2,c3
having count(src1) <> count(src2);

回答

也许我们可以使用维恩图(手工)绘制查询和结果,并查看它们是否生成相同的图。维恩图非常适合表示数据集,而SQL查询可处理数据集。绘制维恩图可能有助于我们可视化两个查询在功能上是否等效。

回答

这很容易做到。

假设查询分别命名为a和b

一种

b

应该给你一个空的集。如果没有。然后查询将返回不同的集合,结果集将为我们显示不同的行。

然后做

b

一种

那应该给你一个空集。如果是这样,则查询确实返回相同的集合。
如果不为空,则查询在某些方面会有所不同,结果集将为我们显示不同的行。

回答

这将达到目的。如果此查询返回零行,则两个查询将返回相同的结果。另外,它可以作为单个查询运行,因此我们不必担心设置隔离级别,以使数据不会在两个查询之间发生变化。

select * from ((<query 1> MINUS <query 2>) UNION ALL (<query 2> MINUS <query 1>))

这是一个方便的shell脚本来执行此操作:

#!/bin/sh

CONNSTR=
echo query 1, no semicolon, eof to end:; Q1=`cat` 
echo query 2, no semicolon, eof to end:; Q2=`cat`

T="(($Q1 MINUS $Q2) UNION ALL ($Q2 MINUS $Q1));"

echo select 'count(*)' from $T | sqlplus -S -L $CONNSTR