往期回顾:
左右SQL执行计划妙招 ③ ——执行计划SQL写法差异改变【with子句】
左右SQL执行计划妙招 ④ ——执行计划SQL写法差异改变【insert all】
左右SQL执行计划妙招 ⑤ ——执行计划利用设计特性改变【并行度影响】
左右SQL执行计划妙招 ⑥ ——执行计划SQL写法差异改变【rownum分页】
左右SQL执行计划妙招 ⑦——执行计划SQL写法差异改变【rownum实体】
左右SQL执行计划妙招 ⑧ ——执行计划SQL写法差异改变【rowid 的影响】
左右SQL执行计划妙招 ⑧ ——执行计划SQL写法差异改变【rowid 的影响】
左右SQL执行计划妙招 ⑨ ——执行计划SQL写法差异改变【缓存结果影响】
左右SQL执行计划妙招 ⑩ ——执行计划SQL写法差异改变【分区条件有无】
左右SQL执行计划妙招⑪ ——执行计划利用设计特性改变【分区设计影响】
左右SQL执行计划妙招 ⑫ ——执行计划利用设计特性改变【Cluster类型】
左右SQL执行计划妙招 ⑬ ——执行计划利用设计特性改变【Iot表类型句】
左右SQL执行计划妙招 ⑭ ——执行计划利用设计特性改变【物化视图影响】
列的空值约束设置是影响Oracle优化器索引选择策略的关键因素。本文作为《左右SQL执行计划妙招》系列第十五篇,通过实际案例展示NOT NULL约束如何改变COUNT(*)查询的执行计划。
drop table t purge; -- 创建测试表,过滤掉空值数据 create table t as select * from dba_objects where object_id is not null; -- 在object_id列上创建索引 create index idx_object_id on t(object_id); set linesize 1000 set pagesize 2000 set autotrace traceonly
语句1:索引列允许空值的查询
-- 语句1(object_id列允许为空) select count(*) from t;
执行计划:
------------------------------------------------------------------- Plan hash value: 2966233522 ------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 292 (1)| 00:00:04 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | TABLE ACCESS FULL| T | 67030 | 292 (1)| 00:00:04 | ------------------------------------------------------------------- 统计信息 ------------------------------------------------------------------- 0 recursive calls 0 db block gets 1047 consistent gets 0 physical reads 0 redo size 425 bytes sent via SQL*Net to client 415 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
语句2:添加NOT NULL约束后的查询
-- 设置object_id列为NOT NULL alter table t modify object_id not null; -- 语句2(相同SQL,但object_id列已设置为NOT NULL) select count(*) from t;
执行计划:
------------------------------------------------------------------------------- Plan hash value: 1131838604 ------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 49 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | INDEX FAST FULL SCAN| IDX_OBJECT_ID | 67030 | 49 (0)| 00:00:01 | ------------------------------------------------------------------------------- 统计信息 ------------------------------------------------------------------------------- 0 recursive calls 0 db block gets 170 consistent gets 0 physical reads 0 redo size 425 bytes sent via SQL*Net to client 415 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
执行计划对比

关键技术原理: Oracle B树索引不存储完全为NULL的条目,当列允许空值时,优化器无法确保索引包含所有记录,因此选择全表扫描保证COUNT(*)结果准确性。
NOT NULL约束的价值: 约束确保索引包含所有记录,优化器可以安全选择索引快速扫描,显著减少IO操作。
约束添加的前提条件
业务逻辑评估: 添加NOT NULL约束必须符合实际业务规则,不能仅为性能优化而强制约束。
-- 正确示例:业务规则本身要求非空 -- 订单ID在业务逻辑上不可能为空 ALTER TABLE orders MODIFY order_id NOT NULL; -- 客户ID对于有效订单记录是必需的 ALTER TABLE orders MODIFY customer_id NOT NULL; -- 错误示例:强制添加不符合业务逻辑的约束 -- 如果评论内容在业务上允许为空,则不应添加约束 -- ALTER TABLE comments MODIFY content NOT NULL; -- 错误做法
NOT NULL约束是查询性能优化的重要手段,但必须在符合业务逻辑的前提下使用。本案例显示,合理的约束设置将COUNT(*)查询的逻辑读从1047次减少到170次,性能提升超过80%。在数据库设计中,应首先确保约束符合业务规则,然后利用这些约束获得查询性能的额外收益。

未完待续..
公众号:收获不止数据库
微信:ljbyxl1213
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/9091385/viewspace-3087768/ ,如需转载,请注明出处,否则将追究法律责任。