【mysql】case...when...、group...by、sum() 聚合函数... 企业组合使用实际场景示例

查询需求场景

-- 统计当月不同流程类型的审批通过流程数、审批终止流程数、审批驳回流程数、新增流程数(归档+终止+退回)、申请总条目数(关联任务单申请条目数总数)

-- 查询思路:
-- 1. 获取当月创建的所有流程  tn_workflow_instance_node(create_time like concat( '2024-09', '%') && node_no = 1 )
-- 2. 按照流程类型进行分组:新增、扩充、冻结、解冻、变更
-- 3. 统计每个组内不同审批状态(终止、退回、归档)总数
-- 4. 关联任务单统计申请条目总数


-- tn_workflow_instance_node 流程实例节点表 (统计流程发起时间在本月内及关联流程实例和任务单用到)
-- tn_workflow_instance 流程实例表 (统计流程的状态是通过、退回、终止时关联用到)
-- tn_work_task 任务单表 (统计流程关联任务单审批的总条目数用到)

预设查询结果

如下:
在这里插入图片描述

SQL实现查询

SELECT
	CASE
		wn.workflow_id 
		WHEN 'WF12304702' THEN
		'扩充' 
		WHEN 'WF182295840' THEN
		'冻结' 
		WHEN 'WF30500982' THEN
		'新增' 
		WHEN 'WF657578194' THEN
		'变更' 
		WHEN 'WF734432211' THEN
		'解冻' 
		ELSE 'Unknown' 
	END AS 流程类型,
	SUM( CASE WHEN wi.STATUS = 'archived' THEN 1 ELSE 0 END ) AS 审批通过总数,
	SUM( CASE WHEN wi.STATUS = 'suspend' THEN 1 ELSE 0 END ) AS 审批中止总数,
	SUM( CASE WHEN wi.STATUS = 'return' THEN 1 ELSE 0 END ) AS 审批退回总数,
	SUM( CASE WHEN wi.STATUS IN ( 'return', 'suspend', 'archived' ) THEN 1 ELSE 0 END ) AS 新增流程总数,
	SUM( wt.apply_count ) AS 任务单申请总条目数 
FROM
	tn_workflow_instance_node wn
	JOIN tn_workflow_instance wi ON wn.workflow_instance_id = wi.workflow_instance_id
	JOIN tn_work_task wt ON wi.task_id = wt.task_id 
WHERE
	wn.create_time LIKE concat( '2024-09', '%' ) 
	AND wn.node_no = 1 
	AND wi.STATUS IN ( 'return', 'suspend', 'archived' ) 
GROUP BY
	wn.workflow_id
	
	

	
	

	

查询结果:

在这里插入图片描述

<think> 我们首先分析用户的问题:用户希望理解和优化SQL语句中的CASE WHEN条件判断部分,具体例子是`CASE WHEN sc.grade>=60 THEN cou.credit ELSE 0 END as credit`。用户可能是在计算学生获得的学分,当成绩大于等于60分时,获得该课程的学分,否则获得0。 根据引用内容,我们可以看到在引用[2]和引用[5]中使用了类似的CASE WHEN语句进行条件统计。特别是引用[2]中,使用了多个CASE WHEN来统计不同分数段的人数。引用[4]展示了在UPDATE语句中使用CASE WHEN进行条件更新。 用户提供的例子是一个典型的条件赋值,它用于在查询结果中根据条件产生不同的值。这种用法在SQL中非常常见,并且通常没有性能问题,因为它只是对每一行数据进行判断。 但是,如果用户担心性能或者有更复杂的逻辑,我们可以考虑优化。不过,在这个简单的例子中,优化空间不大。如果这个查询是在一个大数据集上运行,并且我们只关心学分总和(而不是每一行的明细),那么我们可以考虑在聚合函数使用这个条件表达式,从而避免在每一行都计算(但实际上,聚合函数中也需要对每一行计算,所以可能差别不大)。 另外,用户提供的例子中,`sc.grade`和`cou.credit`分别来自两个表(假设sc是成绩表,cou是课程表),所以可能需要连接操作。如果连接操作导致数据量变大,那么优化连接可能比优化CASE WHEN更重要。 下面我们分步骤解释和优化: 1. 理解原语句的作用: - 当成绩(sc.grade)大于等于60时,返回该课程的学分(cou.credit),否则返回0。 - 这个表达式通常用于计算每个学生每个课程实际获得的学分(通过考试则得学分,否则不得)。 2. 优化建议: a) 确保连接条件正确,并使用索引:由于这个查询可能涉及连接,确保连接字段有索引,以减少连接时间。 b) 如果不需要明细,只需要总学分,那么使用聚合函数(如SUM)来汇总,避免返回大量明细数据。 c) 考虑使用IF函数(如果数据库支持,例如MySQL):在MySQL中,可以使用IF(condition, true_value, false_value)来简化,但CASE WHEN是标准SQL,兼容性更好。 3. 改写示例: 原表达式: CASE WHEN sc.grade>=60 THEN cou.credit ELSE 0 END as credit 如果只需要每个学生的总学分,可以这样写: SELECT sc.student_id, SUM(CASE WHEN sc.grade>=60 THEN cou.credit ELSE 0 END) as total_credit FROM sc JOIN cou ON sc.course_id = cou.course_id GROUP BY sc.student_id 4. 性能考虑: - 在连接表时,确保连接字段有索引(例如sc.course_id, cou.course_id, 以及sc.student_id)。 - 如果成绩表(sc)和课程表(cou)数据量很大,可以考虑在WHERE子句中先过滤掉不需要的行(例如只查询某个学期的数据)。 5. 其他注意事项: - 注意处理NULL值:如果成绩可能是NULL,那么需要决定如何处理。原表达式在成绩为NULL时会进入ELSE分支(即0)。如果希望将NULL视为不及格,这样处理是合适的;如果希望将NULL视为特殊值,可能需要单独处理。 6. 复杂条件优化: - 如果条件非常复杂(例如多个条件嵌套),可以考虑使用视图或者临时表来分步计算,以提高可读性。 综上所述,这个CASE WHEN表达式本身是高效的,优化重点应放在连接操作和索引上。 下面我们生成相关问题: </think> ### SQL中`CASE WHEN`条件判断的用法与优化 #### 1. **基本用法解析** 在SQL语句中: ```sql CASE WHEN sc.grade >= 60 THEN cou.credit ELSE 0 END AS credit ``` - **作用**:根据成绩`grade`的值动态计算学分`credit` - **执行逻辑**: - 若成绩 ≥ 60,返回课程学分`cou.credit` - 若成绩 < 60,返回`0` - **应用场景**:常见于成绩学分转换、条件统计(如引用[2][^2]和引用[5][^5]的分段统计) #### 2. **性能优化方案** 当数据量较大时,可通过以下方法优化: ##### (1) **减少数据扫描范围** 添加筛选条件,避免全表扫描: ```sql SELECT CASE WHEN sc.grade >= 60 THEN cou.credit ELSE 0 END AS credit FROM score sc JOIN course cou ON sc.course_id = cou.id WHERE sc.grade IS NOT NULL -- 排除无效数据 AND sc.semester = '2023-Fall' -- 限定范围 ``` ##### (2) **避免重复计算** 若需聚合统计,直接在`SUM()`中嵌入`CASE WHEN`: ```sql SELECT SUM(CASE WHEN sc.grade >= 60 THEN cou.credit ELSE 0 END) AS total_credit FROM score sc JOIN course cou ON sc.course_id = cou.id ``` > ✅ **优点**:单次遍历完成计算(引用[5][^5]的优化方案) ##### (3) **索引优化** 对常用筛选字段创建索引: ```sql CREATE INDEX idx_grade ON score(grade); -- 成绩字段索引 CREATE INDEX idx_course ON course(id, credit);-- 课程表复合索引 ``` ##### (4) **简化条件逻辑** 若只有单条件判断,可改用`IF()`函数MySQL/Oracle适用): ```sql SELECT IF(sc.grade >= 60, cou.credit, 0) AS credit -- 更简洁的写法 ``` #### 3. **常见问题处理** - **NULL值处理**: ```sql CASE WHEN sc.grade >= 60 THEN cou.credit WHEN sc.grade IS NULL THEN NULL -- 明确处理NULL ELSE 0 END ``` - **多条件扩展**(如引用[4][^4]的更新操作): ```sql CASE WHEN grade >= 90 THEN credit * 1.2 -- 优秀奖励学分 WHEN grade >= 60 THEN credit ELSE 0 END ``` #### 4. **与连接查询的对比** 用户提到的`LEFT JOIN`方案存在缺陷: ```sql -- 不推荐写法(可能遗漏无成绩记录) SELECT a.date, a.win, b.lose FROM ... LEFT JOIN ... ``` ✅ **推荐**:使用`CASE WHEN`单次分组聚合(如引用[2][^2]),避免连接风险和NULL值问题。 --- ### 相关问题 1. 如何在`SUM()`函数中嵌入`CASE WHEN`实现条件聚合? 2. 多层级条件判断(如优/良/及格)时,`CASE WHEN`如何优化可读性? 3. 对于超大数据集,除了索引还有哪些提升`CASE WHEN`性能的策略? 4. `CASE WHEN`与`IFNULL()`/`COALESCE()`在处理NULL时有何区别? [^2]: SQL常考笔试题[转] [^4]: mysql case when [^5]: SQL 练习
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码元宋大米

感谢小主大赏,留言可进互助群~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值