一条sql语句同时返回已发布和未发布的数量

发布时间:2026/5/21 11:14:20

一条sql语句同时返回已发布和未发布的数量 通过条件聚合Conditional Aggregation可以在一条 SQL 中统计不同状态的数量避免多次查询数据库提高性能并保证数据一致性。以下是具体的实现方案涵盖 SQL 原理、MyBatis Plus 集成方式及注意事项。一、SQL 核心原理使用SUM(CASE WHEN ...)或COUNT(CASE WHEN ...)来实现。1. 标准写法兼容所有数据库SELECT SUM(CASE WHEN status 1 THEN 1 ELSE 0 END) AS published_count, SUM(CASE WHEN status 0 THEN 1 ELSE 0 END) AS unpublished_count FROM article WHERE delete_flag 0;2. MySQL 简化写法MySQL 中布尔表达式结果为 1 或 0可以直接相加SELECT SUM(status 1) AS published_count, SUM(status 0) AS unpublished_count FROM article WHERE delete_flag 0;⚠️ 注意空值处理如果没有数据SUM返回的是NULL而不是0。建议在 SQL 中用IFNULL或COALESCE处理或者在 Java 代码中处理。二、MyBatis Plus 实现步骤由于 MyBatis Plus 的selectCount只能返回一个数值这种多列聚合查询必须使用自定义 SQL。1. 定义 DTO 接收结果不要直接用 Entity建议创建一个专门的 DTO。Data public class StatusCountDTO { private Integer publishedCount; // 已发布数量 private Integer unpublishedCount; // 未发布数量 }2. Mapper 接口定义在继承BaseMapper的接口中添加自定义方法。Mapper public interface ArticleMapper extends BaseMapperArticle { // 方式 A注解方式简单 SQL Select(SELECT IFNULL(SUM(CASE WHEN status 1 THEN 1 ELSE 0 END), 0) AS publishedCount, IFNULL(SUM(CASE WHEN status 0 THEN 1 ELSE 0 END), 0) AS unpublishedCount FROM article WHERE delete_flag 0) StatusCountDTO selectStatusCount(); // 方式 BXML 方式推荐便于维护 StatusCountDTO selectStatusCountXml(); }3. XML 配置推荐在resources/mapper/ArticleMapper.xml中mapper namespacecom.example.mapper.ArticleMapper select idselectStatusCountXml resultTypecom.example.dto.StatusCountDTO SELECT IFNULL(SUM(CASE WHEN status 1 THEN 1 ELSE 0 END), 0) AS publishedCount, IFNULL(SUM(CASE WHEN status 0 THEN 1 ELSE 0 END), 0) AS unpublishedCount FROM article WHERE delete_flag 0 !-- 可以动态添加其他条件 -- if testcategoryId ! null AND category_id #{categoryId} /if /select /mapper注意如果 XML 中需要动态参数接口方法需加上Param。4. 调用测试StatusCountDTO count articleMapper.selectStatusCount(); System.out.println(已发布 count.getPublishedCount()); System.out.println(未发布 count.getUnpublishedCount());三、为什么不用 QueryWrapper 实现很多开发者尝试用QueryWrapper做这件事但会遇到困难返回类型限制selectList返回ListEntityselectCount返回Long。无法直接返回包含两个计数值的 DTO。强行实现的代价方案 1查两次selectCount性能差非原子操作。方案 2selectList查出所有数据在 Java 内存中统计内存溢出风险性能极差。方案 3selectMaps返回MapString, Object类型不安全代码丑陋。结论这种统计需求自定义 SQL DTO 是最佳实践。四、进阶场景带条件的统计如果需要根据用户筛选条件如时间范围、分类 ID动态统计XML 方式更灵活select idselectStatusCountByCondition resultTypecom.example.dto.StatusCountDTO SELECT IFNULL(SUM(CASE WHEN status 1 THEN 1 ELSE 0 END), 0) AS publishedCount, IFNULL(SUM(CASE WHEN status 0 THEN 1 ELSE 0 END), 0) AS unpublishedCount FROM article where delete_flag 0 if teststartTime ! null AND create_time #{startTime} /if if testendTime ! null AND create_time lt; #{endTime} /if /where /select五、性能优化建议索引确保status和delete_flag字段上有索引或者建立联合索引(delete_flag, status)这样数据库可以直接走索引统计无需回表。缓存如果数据量极大且统计频率高实时 SQL 可能慢。可以考虑使用 Redis 缓存计数发布时 1下架时 -1。使用定时任务统计表到单独的统计表里。NULL 值处理务必在 SQL 中使用IFNULL(..., 0)或COALESCE(..., 0)否则 Java 中接收到的可能是null导致自动拆箱报错Integer转int时。总结一条 SQL 同时返回多个状态的数量是完全可行的核心是使用SUM(CASE WHEN...)。在 MyBatis Plus 中请避开QueryWrapper直接使用自定义 Mapper 方法 DTO来实现既高效又优雅。

相关新闻