编程后端开发者

如何在 SQL 中实现条件聚合(例如,在一个查询中按不同状态计算总和),以及此处存在哪些潜在问题?

用 Hintsage AI 助手通过面试

答案。

条件聚合 在需要在一个查询中根据不同条件聚合值(例如总和或数量)时使用。历史上,为了达到类似目的,SQL 开发者不得不编写多个子查询或针对每个类别单独进行连接和分组,这导致代码不够可读且性能较低。

问题在于同时计算多个具有不同条件的聚合是困难的 — 每个单独的聚合通常需要过滤,而简单的应用 SUM()COUNT() 并不考虑聚合函数内部所需的条件。

解决方案 — 在聚合函数中使用 CASE 结构,这样可以在不进行多个连接的情况下“分隔”不同条件下的聚合:

代码示例:

SELECT department, SUM(CASE WHEN status = 'approved' THEN amount ELSE 0 END) AS approved_sum, SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS pending_sum, COUNT(CASE WHEN status = 'rejected' THEN 1 END) AS rejected_count FROM payments GROUP BY department;

关键特点:

  • 允许在一个查询中同时对多个条件进行聚合。
  • 在所有 SQL 方言中有效(具体语法支持可能有所不同)。
  • 不仅可以与 SUM 一起使用,还可以与 COUNT、AVG 等使用。

异常问题。

是否可以在条件聚合中不使用 CASE 结构?

不正确的答案是直接在查询中使用 WHERE 与聚合函数一起。实际上,WHERE 在聚合之前过滤行,而不是在每个聚合列内部。

代码示例(错误方式):

SELECT COUNT(*) FROM payments WHERE status = 'approved'; SELECT COUNT(*) FROM payments WHERE status = 'pending';

这些查询不能在不同条件下粘合成一行结果而不使用 CASE。

如果在条件表达式中使用 NULL,会发生什么?

如果 CASE 不返回替代值,则默认情况下它会为 NULL,而聚合函数会忽略 NULL。

代码示例:

SUM(CASE WHEN status = 'approved' THEN amount END) -- 如果状态不是 'approved',则为 NULL,导致在 SUM 中忽略该行

可以使用 IF 来替代 CASE 吗?

在某些 SQL 方言中(例如,MySQL),这是可能的,但这种代码在不同数据库之间的可移植性较差。在通用查询中始终使用 CASE。

常见错误和反模式

  • 使用 WHERE 而不是在 CASE 内部的嵌套逻辑会导致需要执行多个单一查询,这会降低性能。
  • 忘记在 CASE 中设置 ELSE 0 可能导致因 NULL 而产生不正确的数量/总和。
  • 复制粘贴 CASE 而不进行必要的检查会导致逻辑错误和不正确的计算。

生活中的示例

负面案例

分析师试图通过编写多个单独的子查询来按每个状态计算总和。外部报告变得复杂,添加新状态每次都需要重写代码。

优点:

  • 对于一个指标实现简单。 缺点:
  • 难以扩展,性能低下,代码重复性高。

正面案例

开发者使用 CASE 在一个通用查询中,创建了一个可以扩展的新状态的通用报告,简单编辑一个查询即可。

优点:

  • 可读性高,扩展性强,性能良好。 缺点:
  • 需要了解 CASE 语法及其在不同数据库中的细微差别。