优化04-选择率和直方图

选择率

在Oracle数据库中,选择率(Selectivity) 是优化器(CBO,基于成本的优化器)用来评估SQL语句中某个条件(如WHERE子句)过滤数据的比例的关键指标。它直接影响优化器选择执行计划的策略,例如决定是否使用索引或全表扫描。

选择率表示满足某个条件的行数占总行数的比例

对于等值查询,参考Oracle的数据字典dba_tab_columns的DENSITY和NUM_DISTINCT字段。

col table_name for a20
col column_name for a20
col LOW_VALUE for a20
col HIGH_VALUE for a20
set line 400
select table_name,column_name,num_distinct,density,NUM_NULLS,LOW_VALUE,HIGH_VALUE
from dba_tab_columns where table_name='EMP';TABLE_NAME           COLUMN_NAME          NUM_DISTINCT    DENSITY  NUM_NULLS LOW_VALUE            HIGH_VALUE
-------------------- -------------------- ------------ ---------- ---------- -------------------- --------------------
EMP                  EMPNO                          14 .071428571          0 C24A46               C25023
EMP                  ENAME                          14 .071428571          0 4144414D53           57415244
EMP                  JOB                             5         .2          0 414E414C595354       53414C45534D414E
EMP                  MGR                             6 .038461538          1 C24C43               C25003
EMP                  HIREDATE                       13 .076923077          0 77B40C11010101       77BB0517010101
EMP                  SAL                            12 .083333333          0 C209                 C233
EMP                  COMM                            4        .25         10 80                   C20F
EMP                  DEPTNO                          3 .333333333          0 C10B                 C11F

对于等值查询,如果该列没有空值和直方图统计信息,选择率就是DENSITY的值或(1/NUM_DISTINCT);如果有空值,则可选择率为:(1/NUM_DISTINCT)*(NUM_ROWS-NUM_NULLS)/NUM_ROWS

对于范围查询,选择率的计算方法就在上述基础上加入最大值和最小值的统计信息,这里就不多做赘述。

选择率和索引

选择率影响着一个SQL的执行计划,准确的来说,选择率影响表的访问方式(即全表扫描还是索引扫描)。Oracle的SQL优化器是基于成本的,我们称为CBO,CBO会依据选择率来确定对某一数据集的访问的成本(COST),从而选择成本最低的访问方式。

例如,表A有8行数据,在表A上对列col1有索引,列col1上有8个不同值,如果SQLA的谓词条件为col1的等值查询,对与SQLA的最优执行计划,CBO会选择索引扫描;如果表A对列col2有索引,列col2上只有2个不同值,如果SQLB的谓词条件为col2的等值查询,对与SQLB的最优执行计划,CBO可能会选择全表扫描,因为索引扫描的寻找叶子块+回表的成本可能会大于全表扫描的成本。

下面我们做一下选择率的测试

--创建表和索引
create table tab1(id int,name varchar2(10),gender varchar2(5));
create index idx_id on tab1(id);
create index idx_gender on tab1(gender);
--插入2000条数据,id列从1递增,name列为随机的5个字符串,gender列为随机的‘f’或‘m’。
DECLARE-- 定义记录类型和集合类型TYPE t_employee IS RECORD (id       NUMBER,name     VARCHAR2(5),gender   CHAR(1));TYPE t_employee_tbl IS TABLE OF t_employee;v_data t_employee_tbl := t_employee_tbl(); -- 初始化集合
BEGIN-- 批量生成测试数据(200行)SELECT LEVEL AS id,-- 生成5位随机大写字母和数字组合(若只要字母可改用'X'参数)DBMS_RANDOM.STRING('X', 5) AS name,CASE WHEN DBMS_RANDOM.VALUE < 0.5 THEN 'm' ELSE 'f' END AS genderBULK COLLECT INTO v_dataFROM DUALCONNECT BY LEVEL <= 2000;-- 批量插入数据(使用FORALL提升性能)FORALL i IN 1 .. v_data.COUNTINSERT INTO scott.tab1 (id, name, gender)VALUES (v_data(i).id, v_data(i).name, v_data(i).gender);COMMIT; -- 提交事务
EXCEPTIONWHEN OTHERS THENROLLBACK; -- 异常回滚RAISE;
END;
/
--查看统计信息
ANALYZE TABLE scott.tab1 COMPUTE STATISTICS;
col table_name for a10
col column_name for a10
col LOW_VALUE for a20
col HIGH_VALUE for a20
set line 400
select table_name,column_name,num_distinct,density,NUM_NULLS,LOW_VALUE,HIGH_VALUE
from dba_tab_columns where table_name='TAB1';TABLE_NAME COLUMN_NAM NUM_DISTINCT    DENSITY  NUM_NULLS LOW_VALUE            HIGH_VALUE
---------- ---------- ------------ ---------- ---------- -------------------- --------------------
TAB1       ID                 2000      .0005          0 C102                 C215
TAB1       NAME               2000      .0005          0 3030463839           5A5A555138
TAB1       GENDER                2         .5          0 66                   6D
--分别以id和gener列为谓词条件查看
SQL> set autotrace traceonly;
SQL> select * from scott.tab1 where id=6;Execution Plan
----------------------------------------------------------
Plan hash value: 4102116554----------------------------------------------------------------------------------------------
| Id  | Operation                           | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |        |     1 |     9 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TAB1   |     1 |     9 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_ID |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------SQL> select * from scott.tab1 where gender='f';1019 rows selected.Execution Plan
----------------------------------------------------------
Plan hash value: 2211052296--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1000 |  9000 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB1 |  1000 |  9000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

直方图

上面都是该列上数据分布均匀的情况,如果数据分布不均匀,及时统计信息是最新的,但其执行计划可能不是最优的。下面我们测试,

假设一个中学6年级有2000名学生,期中考试分为ABCDE五个等级,其中大部分同学的分数都集中在B,那么查询分数为B的同学信息可能存在执行计划不优的情况。

--创建表和索引
SQL> create table tab2(id int,name varchar2(10),grade char(1));
SQL> create  index grade_idx on tab2(grade);
--插入数据
DECLARE-- 定义记录类型和集合类型TYPE t_student IS RECORD (id     NUMBER,name   VARCHAR2(5),grade  CHAR(1));TYPE t_student_tbl IS TABLE OF t_student;v_data t_student_tbl := t_student_tbl(); -- 初始化集合
BEGIN-- 批量生成测试数据(300行)SELECT LEVEL AS id,-- 生成5位随机大写字母和数字组合(若只要字母可改用'X'参数)DBMS_RANDOM.STRING('X', 5) AS name,CHR(65 + FLOOR(DBMS_RANDOM.VALUE(0,5))) AS grade  -- 生成A-EBULK COLLECT INTO v_dataFROM DUALCONNECT BY LEVEL <= 2000;-- 批量插入数据(使用FORALL提升性能)FORALL i IN 1 .. v_data.COUNTINSERT INTO scott.tab2 (id, name, grade)VALUES (v_data(i).id, v_data(i).name, v_data(i).grade);COMMIT; -- 提交事务
EXCEPTIONWHEN OTHERS THENROLLBACK; -- 异常回滚RAISE;
END;
/把id为50-250的学生分数改为B
update tab2 set grade='B' where id>=50 and id <=2500;
commit;#查看统计信息
SQL> ANALYZE TABLE scott.tab2 COMPUTE STATISTICS;Table analyzed.select table_name,column_name,num_distinct,density,NUM_NULLS,LOW_VALUE,HIGH_VALUE2  from dba_tab_columns where table_name='TAB2';TABLE_NAME COLUMN_NAM NUM_DISTINCT    DENSITY  NUM_NULLS LOW_VALUE            HIGH_VALUE
---------- ---------- ------------ ---------- ---------- -------------------- --------------------
TAB2       ID                 2000      .0005          0 C102                 C215
TAB2       NAME               2000      .0005          0 3031324C58           5A5A543245
TAB2       GRADE                 5         .2          0 41                   45#查询分数为B的学生
SQL> set  autotrace traceonly statistic;
SQL> select * from scott.tab2 where grade='B';1963 rows selected.Execution Plan
----------------------------------------------------------
Plan hash value: 1237454846-------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |           |   400 |  3600 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TAB2      |   400 |  3600 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | GRADE_IDX |   400 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("GRADE"='B')Statistics
----------------------------------------------------------1  recursive calls0  db block gets272  consistent gets0  physical reads0  redo size59224  bytes sent via SQL*Net to client2037  bytes received via SQL*Net from client132  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1963  rows processed
--上面查询采用的索引扫描,逻辑读为272,如果强制让SQL使用全表扫描SQL> select /*+FULL(tab2) */ *  from scott.tab2 where grade='B';1963 rows selected.Execution Plan
----------------------------------------------------------
Plan hash value: 2156729920--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   400 |  3600 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB2 |   400 |  3600 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("GRADE"='B')Statistics
----------------------------------------------------------1  recursive calls0  db block gets138  consistent gets0  physical reads0  redo size54904  bytes sent via SQL*Net to client2037  bytes received via SQL*Net from client132  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1963  rows processed--逻辑读为138,

明明全表扫描的逻辑读更低,为什么CBO还是使用索引扫描的执行计划呢?因为统计信息不知道列grade分布不均匀,安装1/5的选择率生成的执行计划,这种情况可以通过收集列的直方图来解决。

收集之前确定grade没有直方图统计信息

 select table_name,column_name,num_distinct,density,HISTOGRAMfrom dba_tab_columns where table_name='TAB2';
TABLE_NAME COLUMN_NAM NUM_DISTINCT    DENSITY HISTOGRAM
---------- ---------- ------------ ---------- ---------------
TAB2       ID                 2000      .0005 NONE
TAB2       NAME               2000      .0005 NONE
TAB2       GRADE                 5         .2 NONE

收集grade列直方图

EXEC DBMS_STATS.GATHER_TABLE_STATS('SCOTT', 'TAB2', METHOD_OPT => 'FOR COLUMNS GRADE SIZE AUTO');

再次查看grade列统计信息

SQL> select table_name,column_name,num_distinct,density,HISTOGRAM2   from dba_tab_columns where table_name='TAB2';TABLE_NAME COLUMN_NAM NUM_DISTINCT    DENSITY HISTOGRAM
---------- ---------- ------------ ---------- ---------------
TAB2       ID                 2000      .0005 NONE
TAB2       NAME               2000      .0005 NONE
TAB2       GRADE                 5     .00025 FREQUENCY

这时我们再次查看分数为B的学生信息

SQL> set autotrace traceonly;
SQL>  select * from scott.tab2 where grade='B';1963 rows selected.Execution Plan
----------------------------------------------------------
Plan hash value: 2156729920--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1963 | 19630 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB2 |  1963 | 19630 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("GRADE"='B')Statistics
----------------------------------------------------------1  recursive calls0  db block gets138  consistent gets0  physical reads0  redo size54904  bytes sent via SQL*Net to client2037  bytes received via SQL*Net from client132  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1963  rows processed

收集了直方图后,选择了成本更低的执行计划。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/79874.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python实战:通过输入文字匹配在docx文档中的具体位置

在指定的docx文档中,输入一串文字来查看该文字在文档中的具体位置;方便后续处理(如替换文字,高亮显示等等操作) from docx import Documentdef find_text_in_docx(file_path, search_text):# 读取docx文件doc = Document(file_path)# 遍历段落,查找匹配的文本for i

Flutter——数据库Drift开发详细教程(二)

目录 1.核心API1.1查询数据列表分页1.2 列表排序1.3推迟获取与观察 1.核心API 1.1查询数据列表分页 限制返回的结果数量limit&#xff0c;从某一位置开始查询offset ///limit10, offset10 Future<List<TodoItem>> limitTodos(int limit, {int? offset}) {return …

mux-vlan基础配置

1.top配置 2.各个交换机设置 sw3交换机的 sysname swb # undo info-center enable # vlan batch 10 20 30 100 # vlan 10description financial vlan vlan 20description marketing vlan vlan 30description client vlan vlan 100description principal vlanmux-vlansubordi…

SAM详解2(初级应用)

SAM SAM5. 初级应用5.1 静态本质不同子串个数5.2 字符串匹配5.3 关于子串出现次数5.4 动态添加时本质不同子串个数SAM 5. 初级应用 记 l o n g e s t ( x ) longest(x) longest(x) 为点 x x x 代表子串集合中最长串的长度。记 s h o r t e s t ( x ) shortest(x) shortest(…

【日撸 Java 三百行】Day 4(条件语句实战——闰年问题)

目录 Day 4&#xff1a;条件语句实战——闰年问题 一、基础知识及代码思路 二、代码及测试 小结 Day 4&#xff1a;条件语句实战——闰年问题 Task&#xff1a; if 语句的嵌套.基本规律自行百度.布尔类型. 一、基础知识及代码思路 1. 什么是闰年&#xff1f; 闰年是历法中…

MySQL 中 EXISTS (SELECT 1 FROM ...) 的用法详解

EXISTS (SELECT 1 FROM ...) 是 MySQL 中用于存在性检查的核心语法&#xff0c;其核心逻辑是判断子查询是否返回至少一行数据。以下从作用原理、使用场景、性能优化等方面展开解析&#xff0c;并结合具体示例说明。 1. 基本语法与作用原理 语法结构&#xff1a; SELECT 列名 F…

阿里云服务器防御是怎么做出来的?服务器攻击方式有几种?

阿里云服务器防御是怎么做出来的?服务器攻击方式有几种&#xff1f; 服务器防御是一个多层次、多维度的体系&#xff0c;通常包括以下核心措施&#xff1a; 1. 网络层防御 防火墙&#xff08;Firewall&#xff09;&#xff1a;过滤非法流量&#xff0c;仅允许授权通信&#xf…

ElasticSearch深入解析(八):索引设置、索引别名、索引模板

一、索引的动态设置、静态设置 索引设置包含两部分核心内容&#xff1a; 静态设置(static index settings)&#xff0c;只允许在创建索引时或者针对已关闭的索引进行设置。指动态设置(dynamic index settings)&#xff0c;可以借助更新设置(update settings)的方式进行动态更新…

Prometheus实战教程:k8s平台手动部署Grafana

以下是一个可用于生产环境的 Kubernetes 部署 Grafana 的 YAML 文件。该配置包括 Deployment、Service、ConfigMap 和 PersistentVolumeClaim&#xff0c;确保 Grafana 的高可用性和数据持久化。 Grafana 生产部署 YAML 文件 ☆实操示例 cat grafana-deployment.yaml 登录后复制…

VSTO外接程序与VBA的联动尝试

文章目录 前言一、第一坑&#xff1a;安装offic2007后excel加载项找不到了二、示例1 通过Ribbon XML自定义Excel主菜单并添加新项二、示例1 总结三、示例2 创建VSTO外接程序三、示例2 总结四、示例 3 C# VSTO外接程序示例四、示例 3 总结五、实现C# 的VSTO调用VBA函数(xlam)六、…

DeepSeek驱动的金市情绪量化:NLP解析贸易政策文本的情绪传导路径

【AI观察】政策信号与市场情绪的量化关联 基于自然语言处理技术对全球财经文本的情绪分析显示&#xff0c;4月30日亚盘时段现货黄金价格波动率较前日下降12.3%&#xff0c;与技术面修正指标呈现强相关性。特政府最新关税政策调整引发市场风险偏好指数&#xff08;RPI&#xff…

期末代码Python

以下是 学生信息管理系统 的简化版代码示例&#xff08;控制台版本&#xff0c;使用文件存储数据&#xff09;&#xff0c;包含核心功能&#xff1a; 1. 定义学生类 class Student: def __init__(self, sid, name, score): self.sid sid # 学号 self.name name # 姓名 self.s…

zotero pdf中英翻译插件使用

最近发现一个pdf中英翻译的神器zotero-pdf2zh&#xff0c;按照官方安装教程走一遍的时候&#xff0c;发现一些流程不清楚的问题&#xff0c; 此文就是整理一些安装需要的文件以及遇到的问题&#xff1a; 相关文件下载地址 Zotero 是一款免费的、开源的文献管理工具&#xff0…

本地MySQL连接hive

1、首先需要修改MySQL的配置&#xff0c;允许远程连接&#xff1a; # 在本地MySQL服务器上执行 sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf找到 bind-address 行&#xff0c;将其修改为&#xff1a; bind-address 0.0.0.02、在本地MySQL中创建用户并授权&#xff08;注意…

Nginx核心功能2

一&#xff1a;正向代理 正向代理&#xff08;Forward Proxy)是一种位于客户端和原始服务器之间的代理服务器&#xff0c;其主要作用是将客户端的请求转发给目标服务器&#xff0c;并将响应返回给客户端Nginx的正向代理充当客户端的“中间人”&#xff0c;代表用户访问外部资源…

高定电视,一场关于生活方式的觉醒

需要有自己的工作室&#xff0c;雇用3个以上专职模特&#xff0c;至少15名全职员工和20名技术工匠‌&#xff1b; 每年都要参加巴黎高级时装周&#xff0c;展示至少50款原创设计&#xff1b; 使用的面料必须高质量、昂贵且不同寻常&#xff0c;设计上注重细节和个性&#x…

用PyTorch搭建卷积神经网络实现MNIST手写数字识别

用PyTorch搭建卷积神经网络实现MNIST手写数字识别 在深度学习领域&#xff0c;卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;简称CNN&#xff09;是处理图像数据的强大工具。它通过卷积层、池化层和全连接层等组件&#xff0c;自动提取图像特征&#xff…

Tensorrt 基础入门

什么是tensorrt? 其他厂商: Qualcomm, Hailo, google TPU tensorrt的优劣势 使用tensorrt的pipeline tensorrt使用中存在的问题以及解决方案 tensorrt的应用场景 自动驾驶模型部署需要关注的问题&#xff1a; 边端硬件资源有限 散热&#xff08;不能水冷&#xff09; 实时性&…

Qt 显示QRegExp 和 QtXml 不存在问题

QRegExp 和 QtXml 问题 在Qt6 中 已被弃用&#xff1b; 1&#xff09;QRegExp 已被弃用&#xff0c;改用 QRegularExpression Qt5 → Qt6 重大变更&#xff1a;QRegExp 被移到了 Qt5Compat 模块&#xff0c;默认不在 Qt6 核心模块中。 错误类型解决方法QRegExp 找不到改用 Q…

玩玩OCR

一、Tesseract: 1.下载windows版&#xff1a; tesseract 2. 安装并记下路径&#xff0c;等会要填 3.保存.py文件 import pytesseract from PIL import Image def ocr_local_image(image_path):try:pytesseract.pytesseract.tesseract_cmd rD:\Programs\Tesseract-OCR\tesse…