组合索引如何选择前导列的几点考虑
? 选择组合索引的前导列,必须根据具体的业务(SQL)写法和列的数据分布不同而不同,很多书或网上都说,前导列要选择高选择性的,但是,脱离具体的业务,这些是没有意义的, 本文就举一些常见的例子来分析下如何正确选择前导列,以抛砖引玉,实际应用中,有更
?选择组合索引的前导列,必须根据具体的业务(SQL)写法和列的数据分布不同而不同,很多书或网上都说,前导列要选择高选择性的,但是,脱离具体的业务,这些是没有意义的,本文就举一些常见的例子来分析下如何正确选择前导列,以抛砖引玉,实际应用中,有更多复杂的情况需要具体分析。
1.都是等值条件的列,谁做前导列都一样
|
owner有33个不同的值,object_id有75250,显然object_id的选择性更好。但是下面的查询,应用idx1_t与idx2_t的性能一样(COST与CONSISTENT GETS一样)。
dingjun123@ORADB> ?SELECT/* 索引(t idx1_t)*/ * FROM t ? 2 ? WHERE Owner='DINGJUN123' AND object_id=75677; 已选择 1 行。 已过去:00:00:00.00 执行计划 ————————————————————- 计划哈希值:2071967826 ——————————————————————————————– |身份证号?|手术 ? ? ? ? ? ? ? ? ? |姓名 ? |行?|字节 |成本(%CPU)|时间 ? ? | ——————————————————————————————– | ? 0 |选择语句? ? ? ? ? ?| ? ? ? ?| ? ? 1 | ? ?97 | ? ??2?? (0)| 00:00:01 | | ? 1 | ?按索引 ROWID 访问表| T? ? ?| ? ? 1 | ? ?97 | ? ? 2 ? (0)| 00:00:01 | |* ?2 | ?索引范围扫描? ? ? ? ?| IDX1_T | ? ? 1 | ? ? ? | ? ? 1 ? (0)| 00:00:01 | ——————————————————————————————– 谓词信息(通过操作id标识): —————————————————— ? ?2 – 访问(“OWNER”='DINGJUN123' AND “OBJECT_ID”=75677) 统计 ————————————————————- ? ? ? ? ? 1 ?递归调用 ? ? ? ? ? 0 ?db 块获取 ? ? ? ? ??4 ?一致获取 ? ? ? ? ? 0 ?物理读取 ? ? ? ? ? 0 ?重做大小 ? ? ? ?1403 ?通过 SQL*Net 发送到客户端的字节 ? ? ? ? 416 ?通过 SQL*Net 从客户端接收的字节 ? ? ? ? ? 2 ?SQL*Net 往返于客户端 ? ? ? ? ? 0 ?排序(记忆) ? ? ? ? ? 0 ?排序(磁盘) ? ? ? ? ? 1 ?已处理行dingjun123@ORADB> ? SELECT/* 索引(t idx2_t)*/ * FROM t ? 2 ? WHERE Owner='DINGJUN123' AND object_id=75677; 已选择 1 行。 已过去:00:00:00.01 执行计划 ————————————————————- 计划哈希值:3787301248 ——————————————————————————————– |身份证号?|手术 ? ? ? ? ? ? ? ? ? |姓名 ? |行?|字节 |成本(%CPU)|时间 ? ? | ——————————————————————————————– | ? 0 |选择语句? ? ? ? ? ?| ? ? ? ?| ? ? 1 | ? ?97 | ??? 2?? (0)| 00:00:01 | | ? 1 | ?按索引 ROWID 访问表| T? ? ?| ? ? 1 | ? ?97 | ? ? 2 ? (0)| 00:00:01 | |* ?2 | ?索引范围扫描? ? ? ? ?| IDX2_T | ? ? 1 | ? ? ? | ? ? 1 ? (0)| 00:00:01 | ——————————————————————————————– 谓词信息(通过操作id标识): —————————————————— ? ?2 – 访问(“OBJECT_ID”=75677 AND “OWNER”='DINGJUN123′) 统计 ————————————————————- ? ? ? ? ? 1 ?递归调用 ? ? ? ? ? 0 ?db 块获取 ? ? ? ??? 4 ?一致的获取 ? ? ? ? ? 0 ?物理读取 ? ? ? ? ? 0 ?重做大小 ? ? ? ?1403 ?通过 SQL*Net 发送到客户端的字节 ? ? ? ? 416 ?通过 SQL*Net 从客户端接收的字节 ? ? ? ? ? 2 ?SQL*Net 往返于客户端 ? ? ? ? ? 0 ?排序(记忆) ? ? ? ? ? 0 ?排序(磁盘) ? ? ? ? ?已处理 1 行 |
? ? ??虽然如此,但是要记住,这个索引不是仅仅给这2条SQL使用的,事实上可能我们有的查询谓词只有owner或object_id,这时候得考虑使用owner作为前导列还是使用object_id作为前导列。
? ??还有其他引用owner,object_id的情况,比如GROUP BY ,ORDER BY,甚至SELECT…都需要进行整体的分析,这样才能建立最佳的索引。
?
2.有的列是大于(等于)或小于(等于)或者是like 模糊匹配等不等条件,有的列是等值的条件,等值的一般作为前导列更好
–做5次,增加几十万行SYS的进去
INSERT INTO t SELECT * FROM t WHERE owner=’SYS’;
COMMIT;
–重新收集统计信息(省略)
dingjun123@ORADB> SELECT * FROM t ? 2 ?WHERE owner=’DINGJUN123′ ? 3 ?AND object_id>=107889; 1 row selected. Elapsed: 00:00:00.01 Execution Plan ———————————————————- Plan hash value: 2071967826 ————————————————————————————– | Id ?| Operation ? ? ? ? ? ? ? ? ? | Name ? | Rows ?| Bytes | Cost (%CPU)| Time ? ? | ————————————————————————————– | ? 0 | SELECT STATEMENT ? ? ? ? ? ?| ? ? ? ?| ? ? 1 | ? ?96 | ? ? 4 ? (0)| 00:00:01 | | ? 1 | ?TABLE ACCESS BY INDEX ROWID| T ? ? ?| ? ? 1 | ? ?96 | ? ? 4 ? (0)| 00:00:01 | |*??2 | ? INDEX RANGE SCAN ? ? ? ? ?| IDX1_T | ? ? 1 | ? ? ? | ? ? 3 ? (0)| 00:00:01?| ————————————————————————————– Predicate Information (identified by operation id): ————————————————— ???2 – access(“OWNER”=’DINGJUN123′ AND “OBJECT_ID”>=107889 AND “OBJECT_ID” IS ? ? ? ? ? ? ? NOT NULL) Statistics ———————————————————- ? ? ? ? ? 1 ?recursive calls ? ? ? ? ? 0 ?db block gets ? ? ? ??? 5 ?consistent gets ? ? ? ? ? 0 ?physical reads ? ? ? ? ? 0 ?redo size ? ? ? ?1399 ?bytes sent via SQL*Net to client ? ? ? ? 416 ?bytes received via SQL*Net from client ? ? ? ? ? 2 ?SQL*Net roundtrips to/from client ? ? ? ? ? 0 ?sorts (memory) ? ? ? ? ? 0 ?sorts (disk) ? ? ? ? ? 1 ?rows processed |
? ? ??上面的SQL走idx1_t,注意观察谓词,只有access,说明索引完全被利用上,很显然因为owner是前导列,而且是等值查询,按照前导列查询,然后只要分析索引的第2列object_id,当发现不满足条件object_id>=107889之后就停止了,索引扫描没有浪费。
? 2 ?WHERE owner=’DINGJUN123′ ? 3 ?AND object_id>=107889; 1 row selected. Elapsed: 00:00:00.01 Execution Plan ———————————————————- Plan hash value: 3787301248 ————————————————————————————– | Id ?| Operation ? ? ? ? ? ? ? ? ? | Name ? | Rows ?| Bytes | Cost (%CPU)| Time ? ? | ————————————————————————————– | ? 0 | SELECT STATEMENT ? ? ? ? ? ?| ? ? ? ?| ? ? 1 | ? ?96 | ? ? 4 ? (0)| 00:00:01 | | ? 1 | ?TABLE ACCESS BY INDEX ROWID| T ? ? ?| ? ? 1 | ? ?96 | ? ? 4 ? (0)| 00:00:01 | |* ?2 | ? INDEX RANGE SCAN ? ? ? ? ?| IDX2_T | ? ? 1 | ? ? ? | ? ? 3 ? (0)| 00:00:01 | ————————————————————————————– Predicate Information (identified by operation id): ————————————————— ? ?2 – access(“OBJECT_ID”>=107889 AND “OWNER”=’DINGJUN123′ AND “OBJECT_ID” IS ? ? ? ? ? ? ? NOT NULL) ? ??? ?filter(“OWNER”=’DINGJUN123′) Statistics ———————————————————- ? ? ? ? ? 1 ?recursive calls ? ? ? ? ? 0 ?db block gets ? ? ? ? ??5 ?consistent gets ? ? ? ? ? 0 ?physical reads ? ? ? ? ? 0 ?redo size ? ? ? ?1399 ?bytes sent via SQL*Net to client ? ? ? ? 416 ?bytes received via SQL*Net from client ? ? ? ? ? 2 ?SQL*Net roundtrips to/from client ? ? ? ? ? 0 ?sorts (memory) ? ? ? ? ? 0 ?sorts (disk) ? ? ? ? ? 1 ?rows processed |
? ? ??强制使用idx2_t,object_id是前导列,谓词有access,还有filter,说明索引没有被完全利用上,这是因为object_id的不是等值查询,满足object_id>=107889的,按照顺序搜索
所以,中间可能有一些不满足owner=’DINGJUN123′的,还要filter掉。
这种查询和不等值条件作为前导列的查询,一旦object_id>=107889不满足owner=’DINGJUN123′的很多,那么必然造成过多不必要的索引搜索,COST与逻辑读会上升很快,
从而性能急剧下降,因为本例子基本都满足owner条件,所以没有啥浪费。但是下面的例子:
dingjun123@ORADB> SELECT * FROM t ? 2 ?WHERE owner=’DINGJUN123′ ? 3 ?AND object_id>=100; 2540 rows selected. Elapsed: 00:00:00.15 Execution Plan ———————————————————- Plan hash value: 2071967826 ————————————————————————————– | Id ?| Operation ? ? ? ? ? ? ? ? ? | Name ? | Rows ?| Bytes | Cost (%CPU)| Time ? ? | ————————————————————————————– | ? 0 | SELECT STATEMENT ? ? ? ? ? ?| ? ? ? ?| ?2539 | ? 238K| ? 499 ? (0)| 00:00:06 | | ? 1 | ?TABLE ACCESS BY INDEX ROWID| T ? ? ?| ?2539 | ? 238K| ? 499 ? (0)| 00:00:06 | |* ?2 | ? INDEX RANGE SCAN ? ? ? ? ?| IDX1_T | ?2539 | ? ? ? | ? ?12 ? (0)| 00:00:01 | ————————————————————————————– Predicate Information (identified by operation id): ————————————————— ? ?2 – access(“OWNER”=’DINGJUN123′ AND “OBJECT_ID”>=100 AND “OBJECT_ID” IS ? ? ? ? ? ? ? NOT NULL) Statistics ———————————————————- ? ? ? ? ? 1 ?recursive calls ? ? ? ? ? 0 ?db block gets ? ? ? ??527 ?consistent gets ? ? ? ? ?21 ?physical reads ? ? ? ? ? 0 ?redo size ? ? ?268134 ?bytes sent via SQL*Net to client ? ? ? ?2275 ?bytes received via SQL*Net from client ? ? ? ? 171 ?SQL*Net roundtrips to/from client ? ? ? ? ? 0 ?sorts (memory) ? ? ? ? ? 0 ?sorts (disk) ? ? ???2540 ?rows processed |
还是使用idx1_t,没有问题。看下面的强制使用idx2_t的。
? 2 ?WHERE owner=’DINGJUN123′ ? 3 ?AND object_id>=100;2540 rows selected. Elapsed: 00:00:00.33 Execution Plan ———————————————————- Plan hash value: 3787301248 ————————————————————————————– | Id ?| Operation ? ? ? ? ? ? ? ? ? | Name ? | Rows ?| Bytes | Cost (%CPU)| Time ? ? | ————————————————————————————– | ? 0 | SELECT STATEMENT ? ? ? ? ? ?| ? ? ? ?| ?2539 | ? 238K| ?3762 ? (1)| 00:00:46 | | ? 1 | ?TABLE ACCESS BY INDEX ROWID| T ? ? ?| ?2539 | ? 238K| ?3762 ? (1)| 00:00:46 | |* ?2 | ? INDEX RANGE SCAN ? ? ? ? ?| IDX2_T | ?2539 | ? ? ? | ?3274 ? (1)| 00:00:40 | ————————————————————————————– Predicate Information (identified by operation id): ————————————————— ? ?2 – access(“OBJECT_ID”>=100 AND “OWNER”=’DINGJUN123′ AND “OBJECT_ID” IS ? ? ? ? ? ? ? NOT NULL) ? ? ???filter(“OWNER”=’DINGJUN123′) Statistics ———————————————————- ? ? ? ? ? 1 ?recursive calls ? ? ? ? ? 0 ?db block gets ? ? ???3763 ?consistent gets ? ? ? ? ? 0 ?physical reads ? ? ? ? ? 0 ?redo size ? ? ?268134 ?bytes sent via SQL*Net to client ? ? ? ?2275 ?bytes received via SQL*Net from client ? ? ? ? 171 ?SQL*Net roundtrips to/from client ? ? ? ? ? 0 ?sorts (memory) ? ? ? ? ? 0 ?sorts (disk) ? ? ? ?2540 ?rows processed dingjun123@ORADB> SELECT COUNT(*) FROM t WHERE object_id >= 100; ? COUNT(*) ———- ? ?1032649 |
?SELECT COUNT(*) FROM t WHERE object_id >= 100;
返回1032649行,但是WHERE owner=’DINGJUN123′ AND object_id>=100 只返回2540行,要filter掉百万行,轮询索引,造成了极大的浪费。
3.如果都是比较,都是<,>之类的表达式
??这种情况,前导列,根据谓词,选择条件能够定位最接近处理结果的基数,并能够减少索引后filter的工作,因为必然有一列是要走access之后的filter,最好是filter能够过滤较少数据,不要做过多过滤。
??
例如:
dingjun123@ORADB> SELECT * FROM t ? 2 ?WHERE owner>=’DINGJUN123′ ? 3 ?AND object_id>=107872;37 rows selected. Elapsed: 00:00:00.00 Execution Plan ———————————————————- Plan hash value: 3787301248 ————————————————————————————– | Id ?| Operation ? ? ? ? ? ? ? ? ? | Name ? | Rows ?| Bytes | Cost (%CPU)| Time ? ? | ————————————————————————————– | ? 0 | SELECT STATEMENT ? ? ? ? ? ?| ? ? ? ?| ? 205 | 19680 | ???43?? (0)| 00:00:01 | | ? 1 | ?TABLE ACCESS BY INDEX ROWID| T ? ? ?| ? 205 | 19680 | ? ?43 ? (0)| 00:00:01 | |* ?2 | ??INDEX RANGE SCAN ? ? ? ? ?| IDX2_T?| ? 205 | ? ? ? | ? ? 3 ? (0)| 00:00:01 | ————————————————————————————– Predicate Information (identified by operation id): ————————————————— ? ?2 – access(“OBJECT_ID”>=107872 AND “OWNER”>=’DINGJUN123′ AND “OBJECT_ID” ? ? ? ? ? ? ? IS NOT NULL) ? ? ? ?filter(“OWNER”>=’DINGJUN123′) 统计 dingjun123@ORADB>选择 COUNT(*) FROM t WHERE object_id>=107872; |
??关闭index SKIP SCAN,因为所有者种类很少,oracle选择skip SCAN
?更改会话设置“_optimizer_skip_scan_enabled”= false;
?
|

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

请问Wallpaper是否支持家庭共享呢?很遗憾,不能支持哦。尽管如此,我们仍有解决方案。比如,可以用小号购买或先由大号下载好软件和壁纸,然后再更换到小号。简单启动软件是完全没问题的。wallpaperengine能家庭共享吗答:Wallpaper暂不支持家庭共享功能。1、据了解,WallpaperEngine似乎并不适合家庭共享环境。2、为了解决这个困扰,建议您考虑购买全新账号;3、或者先在主账号下载所需软件和壁纸,再切到其他账号。4、只要轻点打开软件,便无碍。5、您可以在上述网页上查看属性“

iBatis与MyBatis:你应该选择哪个?简介:随着Java语言的快速发展,许多持久化框架也应运而生。iBatis和MyBatis是两个备受欢迎的持久化框架,它们都提供了一种简单而高效的数据访问解决方案。本文将介绍iBatis和MyBatis的特点和优势,并给出一些具体的代码示例,帮助你选择合适的框架。iBatis简介:iBatis是一个开源的持久化框架

wallpaperengine是常用于设置桌面壁纸的软件,用户在wallpaperengine里可以搜索自己喜欢的图片来生成桌面壁纸,还支持将电脑中的图片添加到wallpaperengine中设置成电脑壁纸。下面就来看看wallpaperengine设置锁屏壁纸的方法吧。 wallpaperengine设置锁屏壁纸教程 1、首先进入软件,然后选择已安装,点击“配置壁纸选项”。 2、单独设置选择完壁纸后需要点击右下方的确定。 3、再去点击上方的设置选和预览。 4、接下来

用户在使用wallpaperengine可以下载各种壁纸,还可以使用动态壁纸,有很多用户不知道wallpaperengine看片有没有病毒,只是视频文件是无法作为病毒的。wallpaperengine看片有病毒吗答:不会。1、只是视频文件是无法作为病毒的。2、只要确保从可信的来源下载视频,并保持电脑的安全防护措施,就可以避免病毒感染的风险。3、应用程序类壁纸是apk格式,apk可能会携带木马病毒。4、WallpaperEngine本身没有病毒,但是创意工坊里的一些应用程序类壁纸可能有病毒。

用户在使用wallpaper时可以下载各种自己喜欢的壁纸进行使用,有很多用户不知道wallpaper的壁纸在哪个文件夹,用户下载的壁纸存放在content文件夹里。wallpaper的壁纸在哪个文件夹答:content文件夹。1、打开文件资源管理器。2、点击左侧“此电脑”。3、找到“STEAM”文件夹。4、选择“steamapps”。5、点击“workshop”。6、找寻“content”文件夹。

用户在使用wallpaperengine时可以更改自己的电脑壁纸,有很多用户不知道wallpaperengine耗电多吗,动态壁纸是会比静态壁纸更加耗电一点,但耗得不是很多。wallpaperengine耗电多吗答:不多。1、动态壁纸是会比静态壁纸更加耗电一点,但耗得不是很多。2、开启动态壁纸会增加电脑耗电量,并带走一小小部分内存占用。3、用户不需要担心动态壁纸消耗电比较严重的。

想必大家对MicrosoftEdge浏览器并不模式,不过你们知道MicrosoftEdge浏览器怎么更改字体大小吗?下面这篇文章就讲述了MicrosoftEdge浏览器更改字体大小的方法,让我们一起去下文好好学习学习吧。首先,找到MicrosoftEdge浏览器双击打开。可以在桌面快捷键、开始菜单或任务栏找到MicrosoftEdge浏览器,并双击打开。其次,打开【设置】界面打开进入到这个浏览器界面,单击左上角【...】标识;双击【设置】,打开进入设置界面。再次,找到并打开【外观】界面鼠标滚动下

请问怎样查看wallpaper订阅记录呢?许多用户在该软件上进行了大量的订阅,但可能不清楚如何查询这些记录。其实,您只需要在软件的浏览功能区进行操作即可。wallpaperengine订阅记录在哪答:在浏览界面。1、请先启动电脑,并进入wallpaper软件。2、找到应用程序左上方的浏览选项卡图标并点击。3、在“浏览”界面中,您将看到各类壁纸及订阅源的总览。4、在右上角的搜索框中输入您想要搜索的关键词。5、依靠搜索结果,你便能找到订阅壁纸的来源信息。6、点击对应的订阅源,即可进入其网页。7、在订
