【MySQL】索引运算与NULL值问题详解:索引字段应尽量 NOT NULL ,NULL值不能参与部分索引运算

索引运算与NULL值问题详解

不能参与的"部分索引运算"指什么?

这里的"部分索引运算"指的是索引列在某些特定操作或条件下无法被MySQL优化器有效利用的情况,特别是当字段包含NULL值时。主要包括以下几种情况:

1. 比较运算中的问题

sql 复制代码
-- 当字段可能为NULL时,以下比较运算可能无法使用索引:
WHERE indexed_column = NULL  -- 错误写法,应该用 IS NULL
WHERE indexed_column != 'value'  -- 包含NULL的记录会被排除

2. 数学运算和函数

sql 复制代码
-- 对索引列进行运算会导致索引失效
WHERE indexed_column + 1 = 10  -- 索引失效
WHERE YEAR(indexed_date_column) = 2023  -- 索引失效

3. 范围查询中的边界问题

sql 复制代码
-- NULL值在范围查询中的特殊行为
WHERE indexed_column BETWEEN 10 AND 20  -- NULL值不会被包含

为什么NULL值对索引不利?

1. 存储结构差异

  • B+树索引中NULL值会被特殊处理,通常存储在索引的最左侧或最右侧
  • 这使得索引结构变得复杂,影响查询效率

2. 统计信息不准确

  • MySQL的查询优化器依赖索引统计信息做决策
  • NULL值的特殊性质使得统计信息更难准确计算

3. 比较运算的特殊性

  • 在SQL中,NULL = NULL 的结果是UNKNOWN而不是TRUE
  • 这种三值逻辑(TRUE/FALSE/UNKNOWN)增加了优化难度

实际代码演示

示例1:NULL值导致索引失效

sql 复制代码
-- 创建测试表
CREATE TABLE products (
  id INT PRIMARY KEY,
  name VARCHAR(100),
  price DECIMAL(10,2),
  discount DECIMAL(10,2),  -- 允许NULL
  INDEX idx_price (price),
  INDEX idx_discount (discount)
);

-- 查询1:使用索引(非NULL列)
EXPLAIN SELECT * FROM products WHERE price = 19.99;
-- type: ref, key: idx_price

-- 查询2:索引可能失效(NULL列)
EXPLAIN SELECT * FROM products WHERE discount = 0.5;
-- 如果discount列有很多NULL值,优化器可能选择全表扫描

示例2:IS NULL的特殊处理

sql 复制代码
-- 查询3:IS NULL查询
EXPLAIN SELECT * FROM products WHERE discount IS NULL;
-- 即使有索引,IS NULL查询效率通常较低
-- type: ref_or_null(特殊访问方法)

示例3:NOT NULL优化

sql 复制代码
-- 优化后的表结构
CREATE TABLE products_optimized (
  id INT PRIMARY KEY,
  name VARCHAR(100),
  price DECIMAL(10,2) NOT NULL DEFAULT 0,
  discount DECIMAL(10,2) NOT NULL DEFAULT 0,  -- 不允许NULL
  INDEX idx_price (price),
  INDEX idx_discount (discount)
);

-- 查询效率更高
EXPLAIN SELECT * FROM products_optimized WHERE discount = 0.5;
-- type: ref, key: idx_discount

最佳实践建议

  1. 尽量定义NOT NULL约束

    sql 复制代码
    CREATE TABLE users (
      id INT NOT NULL,
      name VARCHAR(100) NOT NULL DEFAULT ''
    );
  2. 为NULL列设置合理的默认值

    sql 复制代码
    ALTER TABLE products MODIFY discount DECIMAL(10,2) NOT NULL DEFAULT 0;
  3. 避免对索引列使用函数或运算

    sql 复制代码
    -- 不好的写法
    SELECT * FROM products WHERE YEAR(created_at) = 2023;
    
    -- 好的写法
    SELECT * FROM products 
    WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31';
  4. 使用IS NULL的特殊处理

    sql 复制代码
    -- 如果必须查询NULL值,考虑使用覆盖索引
    CREATE INDEX idx_discount_covering ON products(discount, name);
  5. 监控NULL值比例

    sql 复制代码
    -- 检查NULL值占比
    SELECT 
      COUNT(*) AS total,
      SUM(CASE WHEN discount IS NULL THEN 1 ELSE 0 END) AS null_count,
      (SUM(CASE WHEN discount IS NULL THEN 1 ELSE 0 END) / COUNT(*)) AS null_ratio
    FROM products;

通过避免NULL值和注意索引运算的限制,可以显著提高索引利用率和查询性能。


https://github.com/0voice

相关推荐
像风一样!3 小时前
MySQL Galera Cluster部署如何实现负载均衡和高可用
数据库·mysql
last_zhiyin4 小时前
Oracle sql tuning guide 翻译 Part 6-4 --- Hint使用准则和Hint使用报告
数据库·sql·oracle·sql tunning
非专业程序员Ping4 小时前
HarfBuzz概览
android·ios·swift·font
chenchihwen5 小时前
AI代码开发宝库系列:FAISS向量数据库
数据库·人工智能·python·faiss·1024程序员节
小光学长5 小时前
基于Vue的课程达成度分析系统t84pzgwk(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
Jeled5 小时前
「高级 Android 架构师成长路线」的第 1 阶段 —— 强化体系与架构思维(Clean Architecture 实战)
android·kotlin·android studio·1024程序员节
摇滚侠5 小时前
全面掌握PostgreSQL关系型数据库,备份和恢复,笔记46和笔记47
java·数据库·笔记·postgresql·1024程序员节
周杰伦fans6 小时前
Navicat - 连接 mysql 、 sqlserver 数据库 步骤与问题解决
数据库·mysql·sqlserver
csdn_aspnet6 小时前
如何在 Ubuntu 24.04/22.04/20.04 上安装 MySQL 8.0
linux·mysql·ubuntu
码以致用7 小时前
StarRocks笔记
数据库·starrocks·olap·1024程序员节