Oracle 列顺序测试
列顺序测试
大家在做表设计的时候通常对表中列的排列顺序没有过多注意,但是其实越常用的列,它的位置越靠前,则查询速度越快。
因为每个block里面存储了row directory (每行数据在块中的位移地址)而没有存放column的位移
所以只能根据column#通过扫描row header 以及 column data 来获取相应的列值
越靠后的列,需要计算的越多
建立测试表col_test
- declare
- v_sql varchar2(4000) ;
- begin
- v_sql := 'create table col_test (' ;
- for i in 1 .. 100 loop
- v_sql := v_sql || 'id'||i||' number ,' ;
- end loop ;
- v_sql := substr(v_sql,1,length(v_sql)-1) || ') ' ;
- execute immediate v_sql ;
- end ;
- /
- _dexter@DAVID> desc col_test ;
- Name Null? Type
- ----------------------------------------------------- -------- --------------
- ID1 NUMBER
- ID2 NUMBER
- ID3 NUMBER
- ID4 NUMBER
- ID5 NUMBER
- ....
- ID99 NUMBER
- ID100 NUMBER
复制代码
初始化数据100w条
- declare
- v_sql varchar2(4000) ;
- begin
- v_sql := 'insert into col_test select ' ;
- for i in 1 .. 100 loop
- v_sql := v_sql || ' 1 ,' ;
- end loop ;
- v_sql := substr(v_sql,1,length(v_sql)-1) || ' from dual connect by level <= 1000000 ' ;
- execute immediate v_sql ;
- commit ;
- end ;
- /
复制代码
测试耗时
- declare
- n number ;
- begin_t pls_integer ;
- end_t pls_integer ;
- v_Sql varchar2(4000) ;
- begin
- for i in 1 .. 101 loop
- begin_t := dbms_utility.get_time ;
- if i = 1 then
- v_sql := 'select count(*) from col_test' ;
- else
- v_sql := 'select count(id'||(i-1)||') from col_test' ;
- end if ;
- execute immediate v_sql into n ;
- end_t := dbms_utility.get_time ;
- dbms_output.put_line('Col'||(i-1)||' : '||(end_t-begin_t)*10);
- end loop ;
- end ;
- /
- --下面的单位为毫秒
- _dexter@DAVID> /
- Col0 : 150
- Col1 : 140
- Col2 : 110
- Col3 : 100
- Col4 : 100
- Col5 : 110
- Col6 : 110
- Col7 : 110
- Col8 : 110
- Col9 : 120
- Col10 : 130
- Col11 : 120
- Col12 : 110
- Col13 : 120
- Col14 : 120
- Col15 : 120
- Col16 : 130
- Col17 : 120
- Col18 : 160
- Col19 : 130
- Col20 : 140
- Col21 : 130
- Col22 : 140
- Col23 : 140
- Col24 : 140
- Col25 : 130
- Col26 : 160
- Col27 : 150
- Col28 : 140
- Col29 : 150
- Col30 : 150
- Col31 : 160
- Col32 : 180
- Col33 : 160
- Col34 : 160
- Col35 : 160
- Col36 : 160
- Col37 : 170
- Col38 : 180
- Col39 : 170
- Col40 : 170
- Col41 : 180
- Col42 : 180
- Col43 : 170
- Col44 : 210
- Col45 : 190
- Col46 : 180
- Col47 : 180
- Col48 : 190
- Col49 : 210
- Col50 : 190
- Col51 : 190
- Col52 : 200
- Col53 : 200
- Col54 : 230
- Col55 : 200
- Col56 : 210
- Col57 : 200
- Col58 : 210
- Col59 : 220
- Col60 : 210
- Col61 : 210
- Col62 : 220
- Col63 : 240
- Col64 : 220
- Col65 : 220
- Col66 : 220
- Col67 : 230
- Col68 : 270
- Col69 : 220
- Col70 : 230
- Col71 : 230
- Col72 : 270
- Col73 : 230
- Col74 : 240
- Col75 : 240
- Col76 : 250
- Col77 : 240
- Col78 : 260
- Col79 : 260
- Col80 : 280
- Col81 : 250
- Col82 : 250
- Col83 : 250
- Col84 : 250
- Col85 : 260
- Col86 : 260
- Col87 : 260
- Col88 : 290
- Col89 : 260
- Col90 : 270
- Col91 : 280
- Col92 : 280
- Col93 : 280
- Col94 : 270
- Col95 : 310
- Col96 : 270
- Col97 : 310
- Col98 : 290
- Col99 : 290
- Col100 : 280
- PL/SQL procedure successfully completed.
复制代码
可以看到耗时是线性增加的,所以在做表设计的时候,应该注意建表时的列的排列顺序,将常用的列放在靠前的位置
null值测试
关于null值的存储,可以查看我的另外一篇帖子http://www.itpub.net/thread-1718328-1-1.html
因为基于它的存储原理,一个rowpiece中,如果列后面的值都为空,则不再占用存储空间,并且 rowheader 的cc(Column Count) 也不会将其记录到上面,
,但是这一行的rowpiece中第二列不为空,第三、四、五列为空
则cc会记为2
个字节的存储空间
null值在表中的位置除了影响存储空间,还会影响查询,因为在扫描数据的时候
可以根据cc的个数来确定是否需要扫描column data的数据
感兴趣的可以做一下测试
100列为有值,其他都为null
下面是测试内容:
建立测试表null_test
- declare
- v_sql varchar2(4000) ;
- begin
- v_sql := 'create table null_test (' ;
- for i in 1 .. 100 loop
- v_sql := v_sql || 'id'||i||' number ,' ;
- end loop ;
- v_sql := substr(v_sql,1,length(v_sql)-1) || ') ' ;
- execute immediate v_sql ;
- end ;
- /
复制代码
初始化数据100w条
- insert into null_test(id50) select 100 from dual connect by level <= 1000000 ; commit ;
复制代码
测试
- declare
- n number ;
- begin_t pls_integer ;
- end_t pls_integer ;
- v_Sql varchar2(4000) ;
- begin
- for i in 1 .. 101 loop
- begin_t := dbms_utility.get_time ;
- if i = 1 then
- v_sql := 'select count(*) from null_test' ;
- else
- v_sql := 'select count(id'||(i-1)||') from null_test' ;
- end if ;
- execute immediate v_sql into n ;
- end_t := dbms_utility.get_time ;
- dbms_output.put_line('Col'||(i-1)||' : '||(end_t-begin_t)*10);
- end loop ;
- end ;
- /
- --测试结果
- dexter@FAKE> /
- Col0 : 60
- Col1 : 80
- Col2 : 80
- Col3 : 90
- Col4 : 90
- Col5 : 100
- Col6 : 100
- Col7 : 100
- Col8 : 120
- Col9 : 110
- Col10 : 120
- Col11 : 130
- Col12 : 130
- Col13 : 140
- Col14 : 150
- Col15 : 160
- Col16 : 160
- Col17 : 160
- Col18 : 170
- Col19 : 170
- Col20 : 170
- Col21 : 180
- Col22 : 180
- Col23 : 180
- Col24 : 190
- Col25 : 190
- Col26 : 200
- Col27 : 200
- Col28 : 200
- Col29 : 200
- Col30 : 200
- Col31 : 210
- Col32 : 210
- Col33 : 220
- Col34 : 220
- Col35 : 230
- Col36 : 220
- Col37 : 220
- Col38 : 230
- Col39 : 240
- Col40 : 240
- Col41 : 230
- Col42 : 240
- Col43 : 250
- Col44 : 250
- Col45 : 250
- Col46 : 260
- Col47 : 260
- Col48 : 270
- Col49 : 260
- Col50 : 270
- Col51 : 70
- Col52 : 80
- Col53 : 70
- Col54 : 70
- Col55 : 80
- Col56 : 70
- Col57 : 70
- Col58 : 80
- Col59 : 70
- Col60 : 70
- Col61 : 70
- Col62 : 70
- Col63 : 80
- Col64 : 80
- Col65 : 70
- Col66 : 70
- Col67 : 70
- Col68 : 80
- Col69 : 70
- Col70 : 70
- Col71 : 70
- Col72 : 80
- Col73 : 70
- Col74 : 70
- Col75 : 70
- Col76 : 80
- Col77 : 70
- Col78 : 70
- Col79 : 70
- Col80 : 80
- Col81 : 70
- Col82 : 70
- Col83 : 80
- Col84 : 70
- Col85 : 70
- Col86 : 70
- Col87 : 80
- Col88 : 70
- Col89 : 70
- Col90 : 80
- Col91 : 70
- Col92 : 70
- Col93 : 70
- Col94 : 80
- Col95 : 70
- Col96 : 70
- Col97 : 70
- Col98 : 70
- Col99 : 70
- Col100 : 80
- PL/SQL procedure successfully completed.
复制代码
列开始到最后都维持到一个稳定的值
线性增加是因为Oracle 数据库会不断扫描row header 以及 column data 计算要查找的列的位移值
列开始,则可以直接扫描row header 的 CC 个数即可拿到要查找的列的值(null),所以节省了大量扫描column data部分的时间,也证实了上面提出的观点
所以设计表的时候,尽量把会存储null值的列放在表的末尾
为了加深印象,我们使用bbed抽出一条数据来看一下它在block中到底是怎样存储的,加深一下印象
- dexter@FAKE> select dbms_rowid.rowid_relative_fno(t.rowid) as "FNO#",
- 2 dbms_rowid.rowid_block_number(t.rowid) as "BLK#",
- 3 dbms_rowid.rowid_row_number(t.rowid) as "ROW#"
- 4 from dexter.null_test t
- 5 where rownum<2 ;
- FNO# BLK# ROW#
- ---------- ---------- ----------
- 6 284 0
- BBED> set dba 6,284
- DBA 0x0180011c (25166108 6,284)
- BBED> p *kdbr[0]
- rowdata[4675]
- -------------
- ub1 rowdata[4675] @5878 0x2c
- BBED> set offset 5878
- OFFSET 5878
- BBED> dump /v offset 5878 count 58
- File: /u01/apps/oracle/oradata/fake/gg_trans201.dbf (6)
- Block: 284 Offsets: 5878 to 5935 Dba:0x0180011c
- -------------------------------------------------------
- 2c0032ff ffffffff ffffffff ffffffff l ,.2.............
- ffffffff ffffffff ffffffff ffffffff l ................
- ffffffff ffffffff ffffffff ffffffff l ................
- ffffffff 02c2022c 0032 l .....Â.,.2
- <16 bytes per line>
复制代码
大家一定对count 58 产生疑问,请继续看下去
row header 2c0032
它包括:
2c=flag=00101100=--H-FL--=header+first+last
00=lb itl slot 0x00
32=cc column count = 16x3+2=50
和列值c202
- dexter@FAKE> select dump(100,16) from dual ;
- DUMP(100,16)
- -----------------
- Typ=2 Len=2: c2,2
复制代码
so 3+49+1+2=55
个字节是下一行的row header : 2c 0032
|
LB |
ITL |
CC |
COL#1 value |
COL#2-49 value |
COL#50 length |
COL#50 value |
COL#51 value |
COL#52-100 value |
|
2C |
00 |
32 |
FF |
FF |
02 |
C202 |
FF |
FF |
附录:
rowpiece构成
可以参考官方文档中的说明
图12-7

row piece header的时候),cluster key (cluster table)
个字节(状态、itl槽位、column个数)
cluster table的存储情况会在其他章节中介绍
列的存储
http://www.itpub.net/thread-1719026-1-1.html
LB说明
KCHDFLPN
K=Cluster key (用来表示cluster key)
C=Cluster table member (当表中拥有cluster时使用此标识)
H=Head of row piece (含有Rowpiece header)
D=Delete row (表示已经删除)
F=First data piece (Rowpiece 的第一个piece)
L=Last data piece (Rowpiece 的最后一个piece,Rowchain或者列个数大于253时候会出现)
P=First column continues from previous piece (没有重现过)
N=Last column continues in next piece (没有重现过)
Oracle 列顺序测试的更多相关文章
- ORACLE中查询语句的执行顺及where部分条件执行顺序测试
Oracle中的一些查询语句及其执行顺序 原文地址:https://www.cnblogs.com/likeju/p/5039115.html 查询条件: 1)LIKE:模糊查询,需要借助两个通配符, ...
- oracle调整表中列顺序
有一个哥们提出一个问题: 有个表,创建时候的列顺序是a,b,c 如何使用select * 的时候,让列的显示顺序是a,c,b 而且任性地必须使用select *来查询,且不能重建表. 假设有个表tes ...
- SQL Server 执行计划利用统计信息对数据行的预估原理二(为什么复合索引列顺序会影响到执行计划对数据行的预估)
本文出处:http://www.cnblogs.com/wy123/p/6008477.html 关于统计信息对数据行数做预估,之前写过对非相关列(单独或者单独的索引列)进行预估时候的算法,参考这里. ...
- SQL Server创建复合索引时,复合索引列顺序对查询的性能影响
说说复合索引 写索引的博客太多了,一直不想动手写,有一下两个原因:一是觉得有炒剩饭的嫌疑,有兄弟曾说:索引吗,只要在查询条件上建索引就行了,真的可以这么暴力吗?二来觉得,索引是个非常大的话题,很难概括 ...
- Oracle列自增-12c
在ORACLE 12C以前的版本中,如果要实现列自增长,需要通过序列+触发器实现,到了12C ORACLE 引进了Identity Columns新特性,从而实现了列自增长功能. 一.Identity ...
- Oracle列自增实现(2)-Identity Columns in Oracle Database 12c Release 1 (12.1)
Oracle列自增-Identity Columns in Oracle Database 12c Release 1 (12.1) 在ORACLE 12C以前的版本中,如果要实现列自增长,需要通过序 ...
- oracle列自增实现(1)-Sequence+Trigger实现Oracle列自增
Sequence+Trigger实现Oracle列自增 序列的语法格式为: CREATE SEQUENCE 序列名 [INCREMENT BY n] [START WITH n] [{MAXVALUE ...
- mysql索引之七:组合索引中选择合适的索引列顺序
组合索引(concatenated index):由多个列构成的索引,如create index idx_emp on emp(col1, col2, col3, ……),则我们称idx_emp索引为 ...
- Oracle Flushback 学习测试
Oracle Flushback 学习测试:三思笔记 Flashback恢复 从9i开始,利用oracle查询的多版本一致的特点,实现从回滚段中读取一定时间内在表中操作的数据,被称为 flashbac ...
随机推荐
- NDK中, 如何提高脚本式语言的可读性
原文来自安卓教程网android.662p.com,转载时请注明文章的来源:http://android.662p.com/thread-5245-1-1.html [概述] NDK开发中, ...
- Mysql 作业(Scheduler)
200 ? "200px" : this.width)!important;} --> 介绍 作业也叫做事件调度,其实它也就是一个时间触发器:它可以定义某个时间点执行指定的数 ...
- jq实现手机自定义弹出输入框
手机涉及到填写表单时,需要手机弹出自定义的输入框,而非手机自带的输入键盘,如大写数字等. 实现思路(考虑多种文本输入形式): 首先,文本框获取焦点时禁止手机弹出自带的输入键盘. // 禁用手机自带的键 ...
- c语言文件操作函数详解
一.文件操作注意点: 1 打开文件时,如果打开方式加“+”,表示该文件可以“写” ; 2 退出程序一般用exit函数,正常退出参数为0,非正常退出参数为正零值 ; 3 文件的读写操作:按字符.字符串. ...
- sqlplus中"-S"和"-L"用法
Usage: SQLPLUS [option] [logon] [start] <option> ::= -H | -V | [ [-L] [-M ] [-R ] [-S] ] &qu ...
- Linux下crontab详解
1.crond介绍 crond是Linux下的任务调度命令,让系统定期执行指定程序.crond命令每分钟都会检查是否有要执行的工作,若有要执行的程序便会自动执行. linux下任务调度工作主要分两类: ...
- chown
chown 命令 用途:更改与文件关联的所有者或组 chown [ -f ] [ -h ] [ -R ] Owner [ :Group ] { File ... | Directory ... } c ...
- approval workflow in sharepoint designer
http://office.microsoft.com/en-us/sharepoint-designer-help/video-create-an-approval-workflow-in-shar ...
- 类似nike+、香蕉打卡的转场动画效果-b
首先,支持并感谢@wazrx 的 http://www.jianshu.com/p/45434f73019e和@onevcat 的https://onevcat.com/2013/10/vc-tran ...
- 使用Yeoman搭建 AngularJS 应用 (11) —— 让我们搭建一个网页应用
原文地址:http://yeoman.io/codelab/prepare-production.html 让我们发布这个应用 优化产品的文件 为了创建应用的产品版本,我们想做如下的事情 检查你的代码 ...