您将如何证明两个查询在功能上是等效的,例如,它们总是返回相同的结果集。
由于我在执行此操作时有一个特定的查询,因此我最终按照@dougman 的建议进行,超过大约 10% 的行涉及的表并比较结果,确保没有不合适的结果。
您可以做的最好的事情是根据给定的一组输入比较 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) Cosette 的真实等价证明:
Cosette 检查(通过证明)2 个 SQL 查询是否等价,反例则不等价。这是绝对确定的唯一方法,几乎;)您甚至可以在他们的网站上输入 2 个查询并立即检查(正式)等价性。
链接到珂赛特: https ://cosette.cs.washington.edu/
链接到很好地解释了 Cosette 如何工作的文章:https ://medium.com/@uwdb/introducing-cosette-527898504bd6
2)或者,如果您只是在寻找快速实用的解决方法:
试试这个 stackoverflow 答案:[ sql - 检查两个选择是否相等]
这归结为:
(select * from query1 MINUS select * from query2)
UNION ALL
(select * from query2 MINUS select * from query1)
此查询为您提供仅由其中一个查询返回的所有行。
在我看来,这听起来像是一个 NP 完全问题。我不确定是否有可靠的方法来证明这种事情
这很容易做到。
假设您的查询名为 a 和 b
a减b
应该给你一个空集。如果没有。然后查询返回不同的集合,结果集会显示不同的行。
然后做
b减去a
那应该给你一个空集。如果是这样,那么查询确实返回相同的集合。如果它不为空,则查询在某些方面是不同的,结果集会向您显示不同的行。
DBMS 供应商已经为此工作了很长时间。正如 Rik 所说,这可能是一个棘手的问题,但我认为尚未对问题空间的 NP 完全性进行任何正式分析。
但是,最好的办法是尽可能多地利用 DBMS。所有 DBMS 系统都将 SQL 转换为某种查询计划。您可以使用这个查询计划,它是查询的抽象版本,作为一个很好的起点(DBMS 将进行大量优化,将查询扁平化为更可行的模型)。
注意:现代 DBMS 使用“基于成本”的分析器,它在统计信息更新中是不确定的,因此查询计划器可能会随着时间的推移更改相同查询的查询计划。
在 Oracle(取决于您的版本)中,您可以使用 SQL 提示告诉优化器从基于成本的分析器切换到基于确定性规则的分析器(这将简化计划分析),例如
SELECT /*+RULE*/ FROM yourtable
基于规则的优化器自 8i 以来已被弃用,但即使在 10g 之前它仍然存在(我不知道 'bout 11)。然而,基于规则的分析器没有那么复杂:错误率可能要高得多。
为了进一步阅读更通用的性质,IBM 的查询优化专利相当多产。这里关于将 SQL 转换为“抽象计划”的方法是一个很好的起点: http: //www.patentstorm.us/patents/7333981.html
也许您可以使用Venn Diagrams (手动)绘制查询和结果,并查看它们是否生成相同的图表。维恩图非常适合表示数据集,而 SQL 查询适用于数据集。如果 2 个查询在功能上是等效的,绘制维恩图可能会帮助您可视化。
这会成功的。如果此查询返回零行,则两个查询将返回相同的结果。作为奖励,它作为单个查询运行,因此您不必担心设置隔离级别,以便数据不会在两个查询之间更改。
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
小心!功能上的“等价”往往是基于数据的,你可以通过比较很多情况下的结果来“证明”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.**
你没有。
例如,如果您需要对性能变化没有改变查询输出的高度置信度,那么请对其进行测试。
如果您需要非常高的信心..然后errrm,请进行更多测试。
为 SQL 查询拼凑大量的测试并不难。编写一个过程,它将围绕一组大/完整的可能参数进行迭代,并使用每组参数调用每个查询,并将输出写入相应的表。比较两张表,你就知道了。
这不完全科学,我猜这是 OP 的问题,但我不知道证明等效性的正式方法。