证明SQL查询等效

时间:2008-09-11 15:32:15

标签: sql oracle

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


由于我在做这个时遇到了一个特定的查询,我最终按照@dougman的建议,超过了大约10%的行表并对比结果,确保没有不合适的结果。< / p>

9 个答案:

答案 0 :(得分:13)

您可以做的最好的事情是根据给定的输入集比较2个查询输出,寻找任何差异。要说它们总是会为所有输入返回相同的结果,这取决于数据。

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

答案 1 :(得分:9)

这听起来像是NP完全问题。我不确定是否有一种确定的方法来证明这种事情

答案 2 :(得分:4)

你应该检查珂赛特:它检查(带有证据)2个SQL查询是否相同,而不是等效的反例。这是绝对确定的唯一方法,差不多;)你甚至可以在他们的网站上输入2个查询并立即检查(正式)等价。

珂赛特链接: http://cosette.cs.washington.edu/

链接到可以很好地解释珂赛特如何工作的文章:https://medium.com/@uwdb/introducing-cosette-527898504bd6

如果你想要的只是一个快速的实用修复,你也可以检查这个stackoverflow答案: [sql - check if two select's are equal]

答案 3 :(得分:2)

这很容易做到。

让我们假设您的查询名为a和b

<强>一 减去 B'/强>

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

然后做

<强> b 减去 一个

应该给你一个空集。如果是,则查询返回相同的集合。 如果它不为空,那么查询在某些方面是不同的,结果集会显示不同的行。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

也许您可以使用Venn Diagrams(手动)绘制查询和结果,并查看它们是否生成相同的图表。维恩图适用于表示数据集,而SQL查询适用于数据集。绘制维恩图可能会帮助您可视化2个查询是否在功能上等效。

答案 6 :(得分:1)

这样就可以了。如果此查询返回零行,则两个查询将返回相同的结果。作为奖励,它作为单个查询运行,因此您不必担心设置隔离级别,以便数据在两个查询之间不会更改。

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

这是一个方便的shell脚本:

#!/bin/sh

CONNSTR=$1
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

答案 7 :(得分:0)

你没有。

如果您需要高度自信,例如,性能变化没有改变查询的输出,那么就测试它。

如果你需要非常高的信心......那么错误,再测试一下。

对于SQL查询来说,大量级别的测试并不难以拼凑。编写一个proc,它将迭代一大组/完整的可能参数集,并用每组参数调用每个查询,并将输出写入各自的表。比较两个表,你就可以了。

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

答案 8 :(得分:0)

小心!功能“等价”通常基于数据,您可以通过比较许多情况 的结果来“证明”2个查询的等效性,并且一旦数据以某种方式发生变化仍然是错误的 < /强>

例如:

SQL> create table test_tabA
(
col1 number
)

Table created.

SQL> create table test_tabB
(
col1 number
)

Table created.

SQL> -- insert 1 row

SQL> insert into test_tabA values (1)

1 row created.

SQL> commit

Commit complete.

SQL> -- Not exists query:

SQL> select * from test_tabA a
where not exists
(select 'x' from test_tabB b
where b.col1 = a.col1)

      COL1

----------

         1

1 row selected.

SQL> -- Not IN query:

SQL> select * from test_tabA a
where col1 not in
(select col1
from test_tabB b)

      COL1

----------

         1

1 row selected.


-- THEY MUST BE THE SAME!!! (or maybe not...)


SQL> -- insert a NULL to test_tabB

SQL> insert into test_tabB values (null)

1 row created.

SQL> commit

Commit complete.

SQL> -- Not exists query:

SQL> select * from test_tabA a
where not exists
(select 'x' from test_tabB b
where b.col1 = a.col1)


      COL1

----------

         1

1 row selected.

SQL> -- Not IN query:

SQL> select * from test_tabA a
where col1 not in
(select col1
from test_tabB b)

**no rows selected.**