楼主: wolfop

还是查询优化的问题,可能有点怪

[复制链接]
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
41#
发表于 2021-7-23 21:31 | 只看该作者
jihuyao 发表于 2021-7-23 12:39
I was thinking about the logical flow in procedure way for merge join.  Clearly if a_id is PK and b_ ...

普通索引已经建了,MERGE JOIN就是上面测过的要跑半分钟。

现在测试唯一索引:
drop index idx_range_b_0;
create unique index idx_range_b_0 on range_b(start_id,end_id);

A表原来是随机数,有重复,重新生成数据:
truncate table range_a;

insert into range_a(id)
select level*120 from dual connect by level<=200000;


drop index a_idx;
create unique index a_idx on range_a(id);


再来测试MERGE JOIN计划:

insert into range_result(id,type)
SELECT ID, TYPE
FROM range_a A,range_b B
WHERE A.ID>=B.START_ID AND A.ID<=B.END_ID;

计划和原来一样,时间更长:

117333 rows created.

Elapsed: 00:00:49.29

Plan hash value: 4188551763

-----------------------------------------------------------------------------------------
| Id  | Operation                | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |              |    96M|  1740M|  1437  (70)| 00:00:01 |
|   1 |  LOAD TABLE CONVENTIONAL | RANGE_RESULT |       |       |            |          |
|   2 |   MERGE JOIN             |              |    96M|  1740M|  1437  (70)| 00:00:01 |
|   3 |    SORT JOIN             |              |   200K|  1171K|   443   (1)| 00:00:01 |
|   4 |     INDEX FULL SCAN      | A_IDX        |   200K|  1171K|   443   (1)| 00:00:01 |
|*  5 |    FILTER                |              |       |       |            |          |
|*  6 |     SORT JOIN            |              |  2000 | 26000 |     4  (25)| 00:00:01 |
|   7 |      TABLE ACCESS FULL   | RANGE_B      |  2000 | 26000 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - filter("A"."ID"<="B"."END_ID")
   6 - access(INTERNAL_FUNCTION("A"."ID")>=INTERNAL_FUNCTION("B"."START_ID"))
       filter(INTERNAL_FUNCTION("A"."ID")>=INTERNAL_FUNCTION("B"."START_ID"))
      

使用道具 举报

回复
论坛徽章:
314
行业板块每日发贴之星
日期:2012-07-12 18:47:29双黄蛋
日期:2011-08-12 17:31:04咸鸭蛋
日期:2011-08-18 15:13:51迷宫蛋
日期:2011-08-18 16:58:25紫蛋头
日期:2011-08-31 10:57:28ITPUB十周年纪念徽章
日期:2011-09-27 16:30:47蜘蛛蛋
日期:2011-10-20 15:51:25迷宫蛋
日期:2011-10-29 11:12:59ITPUB十周年纪念徽章
日期:2011-11-01 16:19:41鲜花蛋
日期:2011-11-09 20:33:30
42#
发表于 2021-7-26 14:09 | 只看该作者
newkid 发表于 2021-7-23 21:21
测试数据是刘兄设计的,生成数据的脚本在20楼。

好的,谢谢,我也测试下,

使用道具 举报

回复
论坛徽章:
0
43#
发表于 2021-7-27 15:52 来自手机 | 只看该作者
The unique index on start and end can not ensure B IDs are not overlapped, eg 1, 10 and 2,, 20.  This results lines crossed when being joined even though after sorted.  This can be clearly seen given a traditional merge join, eg, t1 has rows 1,3,5 and t2 has rows 1,5,9 (two joined lines never cross between t1 and t2 and therefore inner loop needs not start from beginning again every time with outer loop going on in comparison with nested loop join).

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
44#
发表于 2021-7-27 21:28 | 只看该作者
jihuyao 发表于 2021-7-27 15:52
The unique index on start and end can not ensure B IDs are not overlapped, eg 1, 10 and 2,, 20.  Thi ...

没有任何一种约束可以实现这种范围的互不覆盖。所以你说的这个算法也不可能被SQL引擎采用。
但是既然数据有索引,已经排过序,反复查找也是很高效的,就像 nested loop 计划所示的那样。

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
45#
发表于 2021-7-27 21:44 | 只看该作者
继续玩,把 PL/SQL 函数改造成SQL MACRO(本来是20C功能,现在19.10也支持)
从原理上说,这是SQL TEXT的替换,不会有上下文切换的开销。

原来标量子查询的做法:
insert into range_result(id,type)
select *
from
(
select id,
    (select case when start_id<=a.id then type else null end as type
        from
        (select /*+ index(range_b idx_range_b_1) */ type,start_id
         from range_b
         where end_id>=a.id
          order by end_id
        ) where rownum=1
    ) as type
from range_a a
) where type is not null;


112086 rows created.

Elapsed: 00:00:01.30


改成SQL MACRO函数:
create or replace function f_get_type (p_id in number) return varchar2
  SQL_MACRO  ------ 关键在这里
as
  v_ret number;
begin
  return
  'select case when start_id<=p_id then type else null end as type
        from
        (select /*+ index(range_b idx_range_b_1) */ type,start_id
         from range_b
         where end_id>=p_id
          order by end_id
        ) where rownum=1';
end f_get_type;
/

----拿出去之后,主查询变得简洁了一些,而且代码也可以在别的查询里重用:
insert into range_result(id,type)
select id,type
from
(
select *
from range_a a
    ,lateral(select * from f_get_type(a.id))
) where type is not null
/

112086 rows created.

Elapsed: 00:00:01.14

使用道具 举报

回复
论坛徽章:
0
46#
发表于 2021-7-28 02:06 来自手机 | 只看该作者
没有任何一种约束可以实现这种范围的互不覆盖。所以你说的这个算法也不可能被SQL引擎采用。  That is true.  I am quite interested in a specific case, eg, t1 ID(1,1,2,5,...) t2 ID(1,1,3,5,1

使用道具 举报

回复
论坛徽章:
0
47#
发表于 2021-7-28 02:10 来自手机 | 只看该作者
没有任何一种约束可以实现这种范围的互不覆盖。所以你说的这个算法也不可能被SQL引擎采用。 That is true.  It has to be a specific test case other than present one in order to see if there are different paths implemented.

使用道具 举报

回复
论坛徽章:
78
ITPUB15周年纪念
日期:2020-08-28 17:23:53双鱼座
日期:2016-03-19 19:38:31秀才
日期:2016-02-18 09:31:52秀才
日期:2016-01-25 15:02:04双子座
日期:2016-01-19 20:35:54秀才
日期:2016-01-13 12:14:26秀才
日期:2015-12-25 15:31:10秀才
日期:2015-12-18 09:28:57秀才
日期:2015-12-14 14:56:09秀才
日期:2015-12-14 14:51:16
48#
 楼主| 发表于 2021-7-30 11:18 | 只看该作者
我日,18年前的坟斗给挖了,出来。当年10GR2的优化器、执行引擎和现在19C的优化器、执行引擎没法比的。

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
49#
发表于 2021-7-30 22:28 | 只看该作者
冤有头债有主,这个坟可不是我挖的,是老刘没法回帖向我求助,我才陪他玩玩。
在我印象中,10g已经是很厉害的存在了,现在还有人用9i呢!我想起地下室里还有一台2003年的win2000笔记本,上面装的是10g, 于是立马挖出来玩。电池坏了,电源还在,难得还能开机,一看内存512M,(当时可是顶配),于是把10g服务启动。但是所有浏览器都上不了现代化的网站了,新浏览器又装不上去,只好用优盘把SQL拷贝到本地执行,结果如下:
原SQL: 2分45秒
标量子查询:10.2.0.3 不支持多层次写法
我构造的hash join方法:1.5秒
加hint: use_nl(b a) 1.3秒

所以我说的没错,10g已经很厉害了,当年发生这个问题,肯定是哪里不对劲。

使用道具 举报

回复
论坛徽章:
2
20周年集字徽章-周
日期:2023-08-03 16:37:4519周年集字徽章-19
日期:2024-09-07 21:32:18
50#
发表于 2021-8-31 11:51 | 只看该作者
我猜想应该是表大小与内存的原因, 我模拟出来的表占用空间不大,完全可以放到内存里. 如果现实中两个表比较大, 内存放不下, 那么merge join就比较慢了.

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表