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

SQL最佳实践:避免使用COUNT=0

如果你遇到类似下面的 SQL 查询:

SELECT *
FROM customer c
WHERE 0 = (SELECT COUNT(*)FROM orders oWHERE o.customer_id = c.customer_id);

意味着有人没有遵循 SQL 最佳实践。该语句的作用是查找没有下过订单的客户,其中子查询使用了 COUNT 函数统计客户的订单数量,如果结果等于零,代表没有订单。

从逻辑上来讲,以上查询没有问题;但是它可能存在性能问题。原因在于 COUNT 函数需要统计订单数量,如果某个客户有大量订单,数据库需要计算出一个总数;即使客户的订单数量不多,仍然需要进行统计。

以下是 MySQL 中的执行计划:

EXPLAIN ANALYZE 
SELECT *
FROM customer c
WHERE 0 = (SELECT COUNT(*)FROM orders oWHERE o.customer_id = c.customer_id);-> Filter: (0 = (select #2))  (cost=111563.21 rows=1040858) (actual time=8.305..7043.840 rows=231117 loops=1)-> Table scan on c  (cost=111563.21 rows=1040858) (actual time=6.968..1104.438 rows=1048577 loops=1)-> Select #2 (subquery in condition; dependent)-> Aggregate: count(0)  (cost=1.30 rows=2) (actual time=0.005..0.005 rows=1 loops=1048577)-> Index lookup on o using customer_id (customer_id=c.customer_id)  (cost=1.15 rows=2) (actual time=0.004..0.005 rows=2 loops=1048577)

其中Aggregate表示子查询中执行了聚合操作,查询的执行时间大概是 7 秒。

实际上这个 COUNT 函数结果对于我们并不重要,我们只需要知道客户是否存在订单。这种情况下,最简单的方法就是使用 EXISTS 运算符:

SELECT *
FROM customer c
WHERE NOT EXISTS (SELECT 1FROM orders oWHERE o.customer_id = c.customer_id);

如果客户存在任何订单,子查询就会获得结果,NOT EXISTS 运算符就会排除对应的客户,从而避免统计全部订单数据。新的执行计划如下:

EXPLAIN ANALYZE 
SELECT *
FROM customer c
WHERE NOT EXISTS (SELECT 1FROM orders oWHERE o.customer_id = c.customer_id);-> Nested loop antijoin  (cost=217780392498.61 rows=2177801765412) (actual time=2273.676..3746.089 rows=231117 loops=1)-> Table scan on c  (cost=111871.61 rows=1040858) (actual time=2.191..804.896 rows=1048577 loops=1)-> Single-row index lookup on <subquery2> using <auto_distinct_key> (customer_id=c.customer_id)  (actual time=0.000..0.000 rows=1 loops=1048577)-> Materialize with deduplication  (cost=423621.04..423621.04 rows=2092314) (actual time=2797.545..2797.545 rows=1021689 loops=1)-> Filter: (o.customer_id is not null)  (cost=214389.64 rows=2092314) (actual time=0.811..1372.007 rows=2097157 loops=1)-> Index scan on o using customer_id  (cost=214389.64 rows=2092314) (actual time=0.809..1164.613 rows=2097157 loops=1)

新的查询计划使用了 antijoin 连接和物化(Materialize),执行时间不到 4 秒。

无论用户订单数量多还是少,NOT EXISTS 的性能都不会差于 COUNT 函数,而且绝大部分情况下它的性能会更好。

http://www.lryc.cn/news/501422.html

相关文章:

  • PG与ORACLE的差距
  • 树莓派3B+驱动开发(2)- LED驱动(传统模式)
  • 超详细搭建PhpStorm+PhpStudy开发环境
  • 分析比对vuex和store模式
  • C# 网络编程--基础核心内容
  • 【C++游戏程序】easyX图形库还原游戏《贪吃蛇大作战》(三)
  • uni-app H5端使用注意事项 【跨端开发系列】
  • SpringBoot中的@Configuration注解
  • 十二、路由、生命周期函数
  • 【蓝桥杯每日一题】X 进制减法
  • 《蓝桥杯比赛规划》
  • C++算法练习day70——53.最大子序和
  • import是如何“占领满屏“
  • ceph /etc/ceph-csi-config/config.json: no such file or directory
  • C语言——验证“哥德巴赫猜想”
  • Flourish笔记:柱状图(Column chart (grouped))
  • 深度学习案例:DenseNet + SE-Net
  • excel文件合并,每个excel名称插入excel列
  • Linux 如何设置特殊权限?
  • 零基础如何使用ChatGPT快速学习Python
  • 【开源】一款基于SpringBoot 的全开源充电桩平台
  • AI - RAG中的状态化管理聊天记录
  • JAVA安全—SpringBoot框架MyBatis注入Thymeleaf模板注入
  • 【STM32系列】提升ADC采样精度的方法
  • 前端面试如何出彩
  • Linux 切换用户的两种方法
  • Spring Boot 3 中Bean的配置和实例化详解
  • Vue实现留言板(实现增删改查)注意:自己引入Vue.js哦
  • IDEA创建Spring Boot项目配置阿里云Spring Initializr Server URL【详细教程-轻松学会】
  • 读取电视剧MP4视频的每一帧,检测出现的每一个人脸并保存