当前位置: 首页 > news >正文

、SEATA分布式事务——XA模式桃

MySQL 中的 count 三兄弟:效率大比拼!

一、快速结论(先看结论再看分析)

方式 作用 效率 一句话总结

count(*) 统计所有行数 最高 我是专业的!我为统计而生

count(1) 统计所有行数 同样高效 我是 count(*) 的马甲兄弟

count(列名) 统计该列非 NULL 的行数 ? 较慢 我挑剔,我只数非空值

结论:用 count(*) 就对了! ?

二、代码示例:亲测三兄弟的差别

准备测试数据

-- 创建测试表

CREATE TABLE user_test (

id INT PRIMARY KEY AUTO_INCREMENT,

name VARCHAR(50),

age INT,

email VARCHAR(100)

);

-- 插入测试数据(故意插入一些NULL值)

INSERT INTO user_test (name, age, email) VALUES

('张三', 25, 'zhangsan@example.com'),

('李四', NULL, NULL),

('王五', 30, 'wangwu@example.com'),

(NULL, 28, 'unknown@example.com'),

('赵六', 35, NULL);

测试查询

-- 查看表中的数据

SELECT * FROM user_test;

/*

+----+--------+------+--------------------+

| id | name | age | email |

+----+--------+------+--------------------+

| 1 | 张三 | 25 | zhangsan@example...|

| 2 | 李四 | NULL | NULL |

| 3 | 王五 | 30 | wangwu@example.com |

| 4 | NULL | 28 | unknown@example.com|

| 5 | 赵六 | 35 | NULL |

+----+--------+------+--------------------+

*/

-- 测试1:count(*) 统计所有行数

SELECT count(*) FROM user_test; -- 结果:5 ?

-- 翻译:"老板,我有多少行数据?全都要!"

-- 测试2:count(1) 统计所有行数

SELECT count(1) FROM user_test; -- 结果:5 ?

-- 翻译:"老板,你给我个固定值1,我数有多少个1"

-- 测试3:count(列名) 统计非NULL的行数

SELECT count(name) FROM user_test; -- 结果:4 ?(NULL的那行没算)

SELECT count(age) FROM user_test; -- 结果:4 ?(NULL的那行没算)

SELECT count(email) FROM user_test; -- 结果:3 ?(两个NULL都没算)

-- 翻译:"我只数有身份证的人,黑户不算"

三、深入剖析:它们到底有啥不同?

1. 语义区别(最重要的区别!)

-- count(*) 是 SQL 标准写法

-- 意思:给我这个表有多少行数据

-- 相当于:"这个会议室有多少个座位?"

-- count(1) 是 count(*) 的一种写法

-- 意思:统计有多少个1

-- 相当于:"给每个座位发个苹果,最后数苹果"

-- count(列名) 是统计该列非NULL值的数量

-- 意思:这个会议室有多少人带了手机

-- 相当于:"检查每个座位,如果有人带了手机就计数"

2. 性能区别(神话与现实)

传说中的误解:

"count(1) 比 count(*) 快" ?

"count(主键) 最快" ?

现实真相:

MySQL 5.7 及以后版本:

count(*) 和 count(1) 性能完全相同

MySQL 优化器会把它们当作一回事

查看执行计划证明:

EXPLAIN SELECT count(*) FROM user_test;

EXPLAIN SELECT count(1) FROM user_test;

EXPLAIN SELECT count(id) FROM user_test;

-- 你会看到:前两个的执行计划完全一样!

性能排序(一般情况):

count(*) ≈ count(1) ?

count(主键列)

count(非主键有索引列) ?

count(非主键无索引列)

为什么 count(列名) 可能更慢?

-- 假设 email 列有索引

SELECT count(email) FROM user_test;

/*

MySQL 需要:

1. 读取索引(如果该列有索引)

2. 检查每个值是否为 NULL

3. 只计数非 NULL 的

如果 email 列没有索引:

1. 读取整行数据(比 count(*) 读的更多)

2. 检查 email 是否为 NULL

3. 只计数非 NULL 的

*/

3. 特殊情况分析

-- 情况1:所有列都不允许NULL

CREATE TABLE user_not_null (

id INT PRIMARY KEY NOT NULL,

name VARCHAR(50) NOT NULL

);

-- 这时候:count(*) = count(id) = count(name)

-- 情况2:空表 vs NULL值

CREATE TABLE empty_table (id INT);

SELECT count(*) FROM empty_table; -- 结果:0

SELECT count(id) FROM empty_table; -- 结果:0

INSERT INTO empty_table VALUES (NULL);

SELECT count(*) FROM empty_table; -- 结果:1

SELECT count(id) FROM empty_table; -- 结果:0 ?

四、实际工作中的选择指南

场景1:统计总行数

-- ? 正确做法

SELECT count(*) FROM orders;

-- ? 错误做法

SELECT count(order_id) FROM orders; -- 万一有NULL呢?

SELECT count(1) FROM orders; -- 能用,但不是标准

场景2:统计有效数据数量

-- 统计有多少用户填写了邮箱

SELECT count(email) FROM users; -- ? 这个场景就该用 count(列名)

-- 统计已完成订单数量(假设 status=2 是已完成)

SELECT count(*) FROM orders WHERE status = 2; -- ?

场景3:统计非重复值

-- 统计有多少个不同的城市

SELECT count(DISTINCT city) FROM users; -- ? count + DISTINCT

-- 统计有多少个城市,排除 NULL

SELECT count(DISTINCT city) FROM users; -- DISTINCT 会自动排除 NULL

五、性能优化技巧

1. 大表优化方案

-- 方案1:使用近似值(适用于统计概览)

SELECT TABLE_ROWS

FROM information_schema.TABLES

WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'big_table';

-- 方案2:分页总数缓存(适用于列表页)

-- 第一次查询时缓存总数,后面定时更新

-- 方案3:使用汇总表

CREATE TABLE stats_daily (

date DATE PRIMARY KEY,

user_count INT,

order_count INT

);

2. 索引优化

-- 为 count(列名) 创建索引

CREATE INDEX idx_email ON users(email);

-- 但注意:count(*) 不一定需要索引,InnoDB有优化

六、有趣比喻帮你记忆

汉堡店排队比喻

-- 有10个人在排队买汉堡

count(*) = "队列里有10个人" ?

count(1) = "我给每人发个号码牌,数有10个牌" ?

count(现金) = "只有8个人带了现金" ?

count(会员卡) = "只有5个人有会员卡" ?

教室点名比喻

-- 教室里有50个座位

count(*) = "教室有50个座位" ?

count(1) = "我在每个座位放本书,最后数有50本" ?

count(学生) = "今天来了45个学生上课" ?(空座位不算)

七、总结与最佳实践

最终建议

统计总行数,一律用 count(*)

这是 SQL 标准写法

性能最优(MySQL有专门优化)

语义最明确

统计某列非 NULL 数量,用 count(列名)

这是它的本职工作

不要用它统计总行数

关于 count(1)

性能与 count(*) 一样

但不够"标准",像方言

建议统一用 count(*)

性能关键点

大表避免频繁 count

考虑使用缓存或汇总表

为 count(列名) 的列加索引

一张图看懂

count(*) -> 总数 -> 最快 -> 推荐使用

count(1) -> 总数 -> 一样快 -> 可用但不标准

count(主键) -> 总数 -> 次快 -> 主键非NULL时可用

count(索引列) -> 非NULL数 -> 较慢 -> 有索引时可用

count(普通列) -> 非NULL数 -> 最慢 -> 谨慎使用

记住口诀:"数总数,用星号;数非空,列名好;数字1,虽高效,不是标准别当宝"忌臣勒偎

http://www.jsqmd.com/news/626671/

相关文章:

  • Go语言的context.WithTimeout超时控制与取消信号在网络编程中的传播
  • CAN BLF包解析实战:从原始报文到可读数据的Python解码之旅
  • 从“能成像”到“像质好”:手把手教你用Zemax优化一个F/4单透镜(附完整操作截图)
  • 07. 装箱与切割问题
  • 别再让FPU等总线了!STM32G474的CCM SRAM实战:把DSP算法速度提升20%的保姆级配置
  • 【笔面试算法学习专栏】KMP算法:字符串匹配的艺术
  • 万字拆解 LLM 运行机制:Token、上下文与采样参数稻
  • Coding Agent底层架构全解(极其详细),吃透6大核心组件,收藏这篇就够了!
  • 打字不如说话,说话不如截图——AI 代码助手的多模态输入实践捶
  • Spring Boot WebFlux 响应式原理
  • 从Windows换到麒麟V10 SP1,这7个自带神器让我彻底卸载了第三方管家软件
  • 08. Spring Boot 工程实践
  • PPO-Lagrangian安全强化学习实战:从原理到代码的深度拆解
  • GLM-. 全面支持与 Gemini CLI 集成:HagiCode 的多模型进化之路屯
  • 【AIOps时代熔断新范式】:融合Prometheus指标、LangChain调用链与强化学习的实时熔断控制器(已落地金融级AI中台)
  • 软件构建管理中的依赖管理优化
  • 从51到32位DSP核:手把手移植你的老8051项目到STC32G144K246(Ai8052U)
  • 09. 性能优化技巧
  • 再次革新 .NET 的构建和发布方式(一)蛊
  • 别再死记公式!图解雅可比迭代与高斯-赛德尔迭代的核心区别与收敛性
  • 告别手动对时!手把手教你用ESP32+手机热点自动获取网络时间(基于ESP-IDF最新框架)
  • 【电价预测】基于深度学习与 SHAP 可解释性分析的西班牙电力市场电价预测研究(Python代码实现)
  • 别再混淆了!手把手教你用Simulink仿真区分双三相与六相PMSM(附互感影响对比)
  • 2026年热门的臭氧老化试验箱用户口碑推荐厂家 - 品牌宣传支持者
  • 【AI Token中转】2026年AI Token代理站搭建实战:技术架构与运营策略
  • 数据库创新探索
  • 基于 MOPGA‑NSGA‑II 的电动车多目标路径优化研究—— 考虑路况、天气与充电约束(Matlab代码实现)
  • Springboot 实现多数据源(PostgreSQL 和 SQL Server)连接椿
  • 终结Agentic RAG乱象!首篇权威SoK论文:从定义、架构到落地的全体系指南
  • CANopen协议栈选型避坑指南:为什么在ZYNQ上我最终选择了CANFestival?