暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

USING bug

原创 胡佳伟 2020-04-01
718

原文作者:Jonathan lewis
原文地址:https://jonathanlewis.wordpress.com/2020/03/18/using-bug/

USING bug

— Jonathan Lewis @ 11:10 am GMT Mar 18,2020

在Oracle开发人员社区论坛上经常看到难以阅读的SQL–有时是因为它是一组混乱的子查询,有时是因为格式不好,有时是因为表和列别名的使用做得不好。最后一个要尤其指出的问题是,在查询过程中多次使用相同的表别名(通常是字母A)。

我已经说过,查询中的每个表都应该有一个不同的别名,并且在查询中使用的每个列也都应该用这个别名(这个URL中的笔记包含了一些改进)。我刚刚发现了另一个原因,为什么这么做是对的,为什么你不应该在一个查询里使用两次相同的别名。下面是在19.3.0.0上测试的一个简化的危害演示:

rem Script: using_bug.sql rem Author: Jonathan Lewis rem Dated: Jan 2020 rem Purpose: rem rem Last tested rem 19.3.0.0 rem create table t2 as select rownum id, object_id, object_name, object_type, rpad('x',100) padding from all_objects where rownum <= 5000 and mod(object_id,2) = 1 / create table t1 as select rownum id, object_id, object_name, object_type, rpad('x',100) padding from all_objects where rownum <= 5000 and mod(object_id,2) = 0 /

我以视图all_objects创建了两个表,其中一个表持有object_id为偶数的行,另一个表持有奇数的行,所以如果我以object_id列将这两个表连接,结果集将是空的。这里有三个连接两个表的查询–我(意外地)在三种情况下都给了两个表相同的别名X:

prompt ======================================= prompt Here's a query that might "confuse" the prompt optimizer when we try to explain it prompt ======================================= explain plan for select max(object_name) from t1 X join t2 X using (object_id); prompt ================================== prompt So might this one, but it doesn't. prompt ================================== explain plan for select max(object_id) from t1 X join t2 X using (object_id); select * from table(dbms_xplan.display); prompt =================================================== prompt With this one A-rows matches E-rows: and it's NOT 0 prompt =================================================== alter session set statistics_level = all; set serveroutput off set linesize 156 select count(*) from t1 X join t2 X using (object_id); select * from table(dbms_xplan.display_cursor(null,null,'cost allstats last'));

在没有明确别名的情况下,第一个查询应该产生一个执行计划;但是,当两个表被赋予相同的别名时,试图explain(或运行)查询会产生错误“ORA-00918: column ambiguously defined”。

第二个查询做得更好,或者更差,这取决于您的观点。名义上,连接是完全有效的,优化器为查询生成一个执行计划。但是该计划预测了一个笛卡尔合并连接,其结果集为2500万行–这看起来不像是一个好的估计–并且该计划没有谓词信息部分。

因此,我们对第三个查询使用count(*)–以防结果集是25万行–并启用rowsource execution statistics,并在运行查询后从内存中获取计划(这将花费将近14秒的时间,CPU会崩溃)。这是输出:

COUNT(*) ---------- 25000000 1 row selected. PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------------------------------------------ SQL_ID 85ygrcg4n3ymz, child number 0 ------------------------------------- select count(*) from t1 X join t2 X using (object_id) Plan hash value: 4259280259 ----------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | Cost (%CPU)| A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem | ----------------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 67358 (100)| 1 |00:00:13.38 | 200 | 198 | | | | | 1 | SORT AGGREGATE | | 1 | 1 | | 1 |00:00:13.38 | 200 | 198 | | | | | 2 | MERGE JOIN CARTESIAN| | 1 | 25M| 67358 (5)| 25M|00:00:10.55 | 200 | 198 | | | | | 3 | TABLE ACCESS FULL | T2 | 1 | 5000 | 15 (7)| 5000 |00:00:00.01 | 100 | 99 | | | | | 4 | BUFFER SORT | | 5000 | 5000 | 67343 (5)| 25M|00:00:04.54 | 100 | 99 | 133K| 133K| 118K (0)| | 5 | TABLE ACCESS FULL | T1 | 1 | 5000 | 13 (0)| 5000 |00:00:00.01 | 100 | 99 | | | | -----------------------------------------------------------------------------------------------------------------------------------------

是的,Oracle成功地生成了2500万行,而不是0行。执行计划是一个笛卡尔合并连接,这是因为优化器丢失了连接谓词(我没有提供谓词信息部分,因为没有谓词信息部分–请注意计划中没有任何操作的星号)。

有趣的是,计划中的查询块/别名部分(当我调用它时)表明的两个别名是X_0001和X_0002,因此Oracle在内部确实设法找到了两个不同的别名–但可能为时已晚。

结论

考虑一下如何合理、安全地使用表别名。修复这个示例很简单,但是一些进入生产环境的混乱SQL可能最终会遇到相同的问题,而不容易发现异常的来源。

脚注

这是Bug 25342699 : WRONG RESULTS WITH ANSI JOIN USING AND IDENTICAL TABLE ALIASES 发布在2017年1月12.1.0.2版本上,还没有修复。

最后修改时间:2020-04-09 16:01:55
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论