Oracle构造序列的方法分析对比

《Oracle构造序列的方法分析对比》要点:
本文介绍了Oracle构造序列的方法分析对比,希望对您有用。如果有疑问,可以联系我们。

编辑手记:关于Oracle的序列,相信大家并不陌生,但很多人平时只用到connect by 的方式来构造序列,今天一起来学习更多的构造序列的方法及每个方法的优缺点.

作者介绍

怀晓明,云和恩墨性能优化专家.ITPUB社区版主,兴趣广泛,视野广阔,目前专注于SQL审核与优化工作,是一个细心敏锐的troubleshooter.擅长数据库和web的设计和开发,精于故障诊断和处理.

正文

Oracle构造序列的方法随着版本一直在变化.在9i之前的版本,常用的方法是:

select rownum rn from all_objects where rownum<=xx;

从all_objects等系统视图中去获取序列的方式,虽然简单,但有一个致命的弱点是该视图的sql非常复杂,嵌套层数很多,一旦应用到真实案例中,极有可能碰到Oracle自身的bug,所以这种方式不考虑,直接pass掉.

2、9i之后,我们用connect by

select rownum rn from dual connect by rownum<=xx;

3、自从10g开始支持XML后,还可以使用以下方式:

select rownum rn from xmltable(‘1 to xx’);

接下来我们从序列大小,构造时间等方面对比分析这两种方式.

1、先看connect by的方法

lastwinner@lw> select count(*) from (select rownum rn from dual connect by rownum<=power(2,19));

COUNT(*)

———-

524288

已用时间:  00: 00: 00.20

lastwinner@lw> select count(*) from (select rownum rn from dual connect by rownum<=power(2,20));

select count(*) from (select rownum rn from dual connect by rownum<=power(2,20))

*

第 1 行出现错误:

ORA-30009: CONNECT BY 操作内存不足

可见直接用connect by去构造较大的序列时,消耗的资源很多,速度也快不到哪儿去.实际上2^20并不是一个很大的数字,就是1M而已.

但xmltable方式就不会耗这么多资源

lastwinner@lw> select count(*) from (select rownum rn from xmltable(‘1 to 1048576’));

COUNT(*)

———-

1048576

已用时间:  00: 00: 00.95

其实除了上述三种办法,我们还可以使用笛卡尔积来构造序列.如果换成笛卡尔连接的方式,那么构造2^20时,connect by也ok

lastwinner@lw> with a as (select rownum rn from dual connect by rownum<=power(2,10))

2  select count(*) from (select rownum rn from a,a);

COUNT(*)

———-

1048576

已用时间:  00: 00: 00.09

我们试着将1M加大到1G,在connect by方式下

lastwinner@lw> with a as (select rownum rn from dual connect by rownum<=power(2,a,a);

COUNT(*)

———-

1073741824

已用时间:  00: 01: 07.37

耗时高达1分钟还多,再看看xmltable方式,考虑到1M的时候耗时就达到0.95秒,因此这里只测试1/16*1G,即64M的情况

lastwinner@lw> select count(*) from (select rownum rn from xmltable(‘1 to 67108864’));

COUNT(*)

———-

67108864

已用时间:  00: 00: 37.00

如果直接构造到1G,那么时间差不多是16*37s这个级别.

但如果通过笛卡尔积+xmltable的方式来构造.

lastwinner@lw> with a as (select rownum rn from xmltable(‘1 to 1024’))

2  select count(*) from (select rownum rn from a,a);

COUNT(*)

———-

1073741824

已用时间:  00: 01: 07.95

这时间和connect by的差不多.以上测试,总的可见,在构造较大序列时,笛卡尔积的方式是最佳的,单纯使用connect by会遭遇内存不足,而单独使用xmltable则会耗费较多的时间.

现在再看看基本用纯表连接的方式来构造同样大小的序列,先来1M的

lastwinner@lw> with b as (select 1 r from dual union all select 2 from dual),

2  c as (select rownum r from b,b,

3  b,

4  b,

5  b,b)

6  select count(*) from c;

COUNT(*)

———-

1048576

已用时间:  00: 00: 00.33

再来64M的

lastwinner@lw> ed

已写入 file afiedt.buf

1  with b as (select 1 r from dual union all select 2 from dual),

6  b,b)

7* select count(*) from c

lastwinner@lw> /

COUNT(*)

———-

67108864

已用时间:  00: 00: 16.62

这个速度并不快,但已经比直接xmltable快了.
其实64M,即64*2^20可以表示为(2^5)^5*2,那我们来改写一下64M的sql

lastwinner@lw> with b as (select 1 r from dual union all select 2 from dual),b),

3  d as (select rownum r from c,c,b)

4  select count(*) from d;

COUNT(*)

———-

67108864

已用时间:  00: 00: 04.53

可以看到,从16s到4s,已经快了很多.这个示例告诉我们,中间表c 在提高速度方面起到了很好的作用.

但在构造到1G时,还是要慢一些

lastwinner@lw> ed

已写入 file afiedt.buf

1  with b as (select 1 r from dual union all select 2 from dual),c)

4* select count(*) from d

lastwinner@lw> /

COUNT(*)

———-

1073741824

已用时间:  00: 01: 11.48

尝试相对较快的写法,多一层中间表

lastwinner@lw> ed

已写入 file afiedt.buf

1  with b as (select 1 r from dual union all select 2 from dual),c),

4  e as (select rownum r from d,d,c)

5* select count(*) from e

lastwinner@lw> /

COUNT(*)

———-

1073741824

已用时间:  00: 01: 06.89

更快一点(思路,32^2=1024,1G=2^30=(2^5)^6=((2^5)^2)^3 .)

lastwinner@lw> ed

已写入 file afiedt.buf

1  with b as (select 1 r from dual union all select 2 from dual),d)

5* select count(*) from e

lastwinner@lw> /

COUNT(*)

———-

1073741824

已用时间:  00: 01: 05.21

这时候我们将2^5=32换成直接构造出来的方式

lastwinner@lw> ed

已写入 file afiedt.buf

1  with b as (select rownum r from dual connect by rownum<=power(2,5)),c)

4* select count(*) from d

lastwinner@lw> /

COUNT(*)

———-

1073741824

已用时间:  00: 01: 05.07

可见所耗费的时间差不多.

由此我们还可以得出,表连接的代价其实也是昂贵的,适当的减少表连接的次数,适当的使用with里的中间表,能有效提高系统性能.

再重复一下刚才构造64M(2^26)的场景

lastwinner@lw> ed

已写入 file afiedt.buf

1  with b as (select 1 r from dual union all select 2 from dual),b)

7* select count(*) from c

lastwinner@lw> /

COUNT(*)

———-

67108864

已用时间:  00: 00: 16.62

总共25次的表连接,1层嵌套,让速度非常慢.提高一下(26=4*3*2+2*2),总共8次表连接,3层嵌套.

lastwinner@lw> ed

已写入 file afiedt.buf

1  with b as (select 1 r from dual union all select 2 from dual),b)

5* select count(*) from e

lastwinner@lw> /

COUNT(*)

———-

67108864

已用时间:  00: 00: 04.00

效率提升4倍.要注意在这个案例中并非表连接越少越好,嵌套层数也是需要关注的指标.执行计划有兴趣的同学自己去看吧,我就不列了,上例中,系统生成的中间表有3个.

最终结论,构造较大序列时,例如同样是构造出64M的序列,oracle在处理时,用表连接的方式明显占优.但考虑到书写的便利性,因此在构造较小序列的时候,比如不超过1K的序列,那么直接用connect by或xmltable的方式就好了.

附:newkid 回复方法,表示更灵活,有兴趣的同学可以尝试:

create or replace function generator (n pls_integer) return sys.odcinumberlist pipelined is
m pls_integer := trunc(n / 10);
r pls_integer := n – 10 * m;
begin
for i in 1 .. m loop
pipe row (null);
pipe row (null);
pipe row (null);
pipe row (null);
pipe row (null);
pipe row (null);
pipe row (null);
pipe row (null);
pipe row (null);
pipe row (null);
end loop;
for i in 1 .. r loop
pipe row (null);
end loop;
end;
/
alter function generator compile plsql_code_type = native;

SQL> select count(*) from table(generator(67108864));

COUNT(*)
———-
67108864

Elapsed: 00:00:06.68

SQL> with b as (select 1 r from dual union all select 2 from dual),
2  c as (select rownum r from b,
3  d as (select rownum r from c,
4  e as (select rownum r from d,b)
5  select count(*) from e;

COUNT(*)
———-
67108864

Elapsed: 00:00:06.32

文章来自微信公众号:数据和云

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


起步 处理器架构,参考 x86是指intel的开发的一种32位指令集 intel和amd早期的cpu都支持这种指令集 AMD比Intel率先制造出了商用的兼容x86的CPU,AMD称之为AMD64 Intel选择了设计一种不兼容x86的全新64为指令集,称之为IA-64,后来支持AMD64的指令集,
pscp pscp -P 22 C:\work\test.txt root@192.168.1.5:/home/data pscp -P 22 root@192.168.1.5:/home/data/test.txt C://work// 检索 find / -name default.config
文件处理 ls -a # 显示所有文件 ls -l # 显示详细信息 ls -d # 显示路径 mkdir /目录名称 # 创建目录 cd /目录名称 # 切换目录 pwd # 显示当前路径 rmdir /目录名称 # 删除目录 cp -rp [目录名称] [目标目录] # 复制目录到目标目录 cp
准备一台电脑(我就用联想拯救者r7000演示) 参考博客制作启动盘 插上U盘,启动电脑,一直按F2 进入如下页面后,将U盘设置为第一启动项,点击exit,保存并退出 之后进入如下页面,选择第三项 进入如下页面,选择第四项 进入如下页面,选择第一项,选中后,先不要点Enter 按e键,将inst.st
认识 Linux系统是参考了UNIX系统作为模板开发的,但没有使用UNIX的代码;是UNIX的一种,但不是衍生版 在Linux内核的基础上开发是发行版 分区 逻辑分区永远从5开始 步骤 挂载:可理解为分配盘符,挂载点即是盘符名;不同之处:Linux中是以空目录名称作为盘符 Hda 第一块硬盘 Hda
文件处理命令 以 . 开头的文件是隐藏文件 以 - 开头表示这是一个文件 以 d 开头表示是一个目录 以 l 开头表示是一个软链接 第一个root是所有者,第二个root是所属组 ls -h 以文件默认大小后缀 显示 ls -i 查看i节点(唯一标识) 所有者:只能有一个,可变更 所属组:只能有一个
参考 01 02 03 前提环境 本地安装VirtualBox,并安装CentOS8,配置网络后,window系统上putty能连接到CentOS8服务器 配置步骤 右键服务器复制 启动复制后的服务器,查看ip和hostname发现和原来的服务器一样,需要修改 hostname # 查看主机名 vi
文件搜索命令 星号匹配任意字符,问号匹配任意单个字符 -iname 根据文件名查找且不区分大小写 -ok 命名会有一个询问的步骤 如果没有找到指定文件,可输入命令:updatedb 更新文件资料库;除tmp目录不在文件资料库收录范围之内 locate -i 文件名 # 检索时不区分大小写 which
安装环境 安装最新版的Virtual Box,点击安装 下载centos8镜像 创建虚拟机,可参考 选择下载到本地的镜像 设置启动顺序 点击启动 启动过程中报错:“FATAL:No bootable medium found!” 1.没有选择iso镜像 2.光驱没有排在第一位置 3.镜像只能选择x8
Linux严格区分大小写 所有内容文件形式保存,包括硬件 Linux不靠扩展名区分文件类型 挂载:将设备文件名和挂载点(盘符)连接的过程 Linux各个目录的作用 bin表示二进制 服务器注意事项 远程服务器不允许关机,只能重启 重启时应该关闭服务 不要在服务器访问高峰运行高负载命令 远程配置防火墙
IDE连接Linux,上传下载文件 参考1 参考2 连接Linux 上传下载文件 本地项目打包后上传 查看是否上传成功,右键下载 补充 后端项目开发完成后,需clean掉临时文件target文件夹,且只推送修改过的文件 前端项目开发的过程中,需要在每个子组件中使用scoped,确保每个子组件中的编码
起步 LTS与普通版本的区别 LTS版本的发布周期更长,更加稳定 安装jdk sudo mkdir /usr/lib/jvm # 在Ubuntu中创建目录 pscp D:\安装包\linux源码包\jdk-8u291-linux-x64.tar.gz chnq@192.168.0.102:/tmp
前言 最近在b站上看了兄弟连老师的Linux教程,非常适合入门:https://www.bilibili.com/video/BV1mW411i7Qf 看完后就自己来试着玩下,正好手上有台空闲的电脑就尝试不使用虚拟机的方式安装Linux系统 安装步骤 制作启动盘 下载ISO镜像,我这里下载的是Cen
新建虚拟电脑 设置内存和处理器 设置硬盘大小 完成 设置 查看光驱 设置启动顺序 点击启动 选择第1项 进入图形安装界面 选择安装位置,开始安装 设置root密码 重启 登录 查看本地文件夹 配置网络,点击设置 查看宿主机ip C:\Users\ychen λ ipconfig 无线局域网适配器 W
源码包安装需手动下载后安装 二进制包则在package目录下 rpm命令管理rpm包 若某个rpm包依赖于某个模块,需要到网站www.rpmfind.net查询该模块依赖的包,安装这个包后自动安装模块,之后就能安装rpm包了 安装升级时使用包全名 查询卸载时使用包名 虚拟机中的Linux系统安装rp
首先进入命令模式,再输入以下命令 命令模式用于输入命令 插入模式可对文件编写操作 编辑模式下的命令是在冒号后输入 :12, 15d # 删除指定范围的行,这里是删除12到15行 :n1,n2s/old/new/g ## 表示从n1行到n2行,old表示旧的字符串 vim使用小技巧:自定义快捷键,如快
使用源码包安装,需要自己指定安装位置,通常是 /usr/local/软件名/ linux中要想启动执行文件,应使用绝对路径 /绝对路径/rpm包名 start ## 执行方式一 service rpm包名 start ## 执行方式二 使用源码包安装后,由于自定义安装路径,就不能使用service命
网络命令 在收邮件的用户中,输入 mail 可查看邮件信息,输入序列号查看详细信息 在mail命令下,输入h 查看所有邮件的列表 输入:d 序列号 # 删除邮件 last # 统计所有用户登录或重启时间,用于日志查询 lastlog # 显示包括未登录用户的登录时间 lastlog -u 用户id
若要使用yum管理,必须能连接网络,首先配置网络IP 进入yum源文件中启动容器 使用yum源头安装rpm包不需要进入package路径,同时也不需要使用包全名,会有yum自动管理 安装软件组
简介 client即是本机安装的docker,相当于git Docker_host相当于centos系统 registry则是docker仓库,相当于GitHub 镜像用于创建docker容器,一个镜像可以创建多个docker容器 容器是由镜像创建的运行实例,(镜像相当于类,容器相当于类创建的对象)