尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Apache Spark SQL 的 CASE 子句(CASE WHEN)完全指南:语法、求值语义与源码实现

Apache Spark SQL 的 CASE 子句(CASE WHEN)完全指南:语法、求值语义与源码实现 大数据数据分析批处理流处理机器学习图计算【免费下载链接】sparkApache Spark - A unified analytics engine for large-scale data processing项目地址https://gitcode.com/gh_mirrors/sp/spark点击查看免费下载导读本文以 docs/sql-ref-syntax-qry-select-case.md 为骨架系统讲解 Apache Spark SQL 中CASE子句的语法、参数约束与实战用法并结合sql/catalyst模块源码揭示其底层求值机制。读完本文你将掌握搜索式与简单式两种CASE写法的区别、ELSE缺省时的 NULL 语义、分支结果类型强制转换规则以及 Spark 是如何在解析期把CASE转换为CaseWhen表达式并生成高效执行代码的。概述CASE 是 SQL 中的 if/elseCASE子句根据指定条件通过一条规则返回特定结果其行为类似于其他编程语言中的if/else语句。在 Spark SQL 中CASE既可用于SELECT列表做条件投影也可嵌套在WHERE、GROUP BY、ORDER BY等任意表达式出现的位置是编写条件逻辑最基础、最常用的语法结构。语法CASE [ expression ] { WHEN boolean_expression THEN then_expression } [ ... ] [ ELSE else_expression ] END语法要点方括号[ expression ]表示可选省略时为搜索式 CASEsearched CASE每个WHEN后直接跟布尔条件给出时为简单式 CASEsimple CASESpark 会把expression与每个WHEN后的取值做等值比较{ ... }表示WHEN ... THEN ...分支可以重复任意次至少一次[ ELSE else_expression ]表示缺省分支可选整个CASE语句必须以END收尾。两种形式的语义对比形式写法条件判定方式源码处理搜索式Searched CASECASE WHEN boolean THEN ... END逐条评估布尔表达式AstBuilder.visitSearchedCase直接构造CaseWhen简单式Simple CASECASE expr WHEN value THEN ... END将expr与各WHEN取值做等值比较AstBuilder.visitSimpleCase将每条分支改写为EqualTo(expr, condition)后构造CaseWhen在 AstBuilder.scala 中可以看到两者的解析差异visitSimpleCase读取CASE后的值表达式e并把每个WHEN condition重写为EqualTo(e, expression(wCtx.condition))——这正是文档示例中简单式被展开成id 100、id CAST((id 300) AS INT)这类等值判断的原因visitSearchedCase直接以WHEN后的表达式作为分支条件不做改写。也就是说简单式CASE在解析阶段就被翻译成了搜索式的等值比较集合两者最终都统一落到同一个CaseWhen表达式上CaseKeyWhen辅助对象即为此而生见 conditionalExpressions.scala。参数详解boolean_expression指定任何计算结果为boolean类型的表达式。两个或多个表达式可通过逻辑运算符AND、OR组合。在搜索式CASE中每个WHEN后必须紧跟这样一个布尔条件在简单式CASE中WHEN后的值会被自动转换为与CASE后表达式做等值比较。then_expression指定当boolean_expression条件成立时返回的表达式。关键约束then_expression与else_expression必须是同一类型或者可以被强制转换为一个公共类型。else_expression指定默认返回的表达式then_expression和else_expression应为同一类型或可强制转换为公共类型。若所有分支条件都不满足且未提供ELSE整个表达式返回NULL。类型约束的源码佐证CaseWhen.checkInputDataTypesconditionalExpressions.scala在分析阶段做两次校验所有分支结果与ELSE结果必须满足TypeCoercion.haveSameType即类型一致或可强制转换到公共类型否则报DATA_DIFF_TYPES错误所有分支条件必须是BooleanType否则报UNEXPECTED_INPUT_TYPE错误并精确指出是第几个分支paramIndex。同时inputTypesForMerging将所有THEN值 ELSE值的数据类型合并考虑见第 191-195 行这保证了CASE结果列的类型推断正确。例如CASE WHEN 1 0 THEN 1 WHEN 2 0 THEN 2.0 ELSE 1.2 END中整型1会被提升为浮点最终结果类型为double返回1.0。求值语义短路求值与缺省 NULL从 CaseWhen.eval 的实现可以确认CASE的核心求值语义从第一个分支开始依次评估条件命中第一个为true的分支即返回对应THEN值并立即停止短路求值不会继续评估后续条件所有条件都不满足时若有ELSE则返回elseValue否则返回null。CaseWhen.nullable第 197-207 行对结果是否可空做了精细推导若存在恒为true的WHEN TRUE分支则只需检查该分支之前各分支的值与首个必然分支的值是否可空否则只要任一分支值可空或ELSE缺省此时视为可空整个表达式就标记为可空——这从类型系统层面保障了无 ELSE 全不匹配时返回 NULL的语义。完整示例以下示例来自原文档含表创建与数据插入可直接在spark-sql或spark-shell中执行验证。准备测试数据CREATE TABLE person (id INT, name STRING, age INT); INSERT INTO person VALUES (100, John, 30), (200, Mary, NULL), (300, Mike, 80), (400, Dan, 50);示例 1搜索式 CASE 做条件分类SELECT id, CASE WHEN id 200 THEN bigger ELSE small END FROM person; -------------------------------------------------------- | id | CASE WHEN (id 200) THEN bigger ELSE small END | -------------------------------------------------------- | 100 | small | | 200 | small | | 300 | bigger | | 400 | bigger | --------------------------------------------------------示例 2简单式 CASE 与混合条件注意类型转换SELECT id, CASE id WHEN 100 then bigger WHEN id 300 THEN 300 ELSE small END FROM person; ----------------------------------------------------------------------------------------------------- | id | CASE WHEN (id 100) THEN bigger WHEN (id CAST((id 300) AS INT)) THEN 300 ELSE small END | ----------------------------------------------------------------------------------------------------- | 100 | bigger | | 200 | small | | 300 | small | | 400 | small | -----------------------------------------------------------------------------------------------------这个例子生动地展示了简单式 CASE 的解析结果CASE id WHEN 100被展开为id 100而WHEN id 300中由于简单式要求做等值比较Spark 会将布尔表达式id 300强制转换为整型CAST((id 300) AS INT)即对id与0/1做比较因此400的id 1并不成立最终落入ELSE返回small。这提醒我们简单式 CASE 只适合等值匹配场景复杂条件请改用搜索式写法。示例 3CASE 嵌套在 WHERE 子句中SELECT * FROM person WHERE CASE 1 1 WHEN 100 THEN big WHEN 200 THEN bigger WHEN 300 THEN biggest ELSE small END small; -------------------- | id | name | age | -------------------- | 100 | John | 30 | | 200 | Mary | NULL | | 300 | Mike | 80 | | 400 | Dan | 50 | --------------------这里CASE 1 1表示将布尔值true与各WHEN取值做等值比较true不会等于100/200/300任一整数值也因此在无ELSE时返回 NULL本例提供了ELSE small于是WHERE条件恒成立返回全部 4 行。它验证了简单式 CASE 的等值比较 类型强制转换语义也展示了CASE作为通用表达式可出现在任意位置。更多实战变体-- 无 ELSE 时全不匹配返回 NULL SELECT CASE WHEN 1 0 THEN 1 WHEN 2 0 THEN 2.0 END; -- 返回 NULL -- 类型提升整型结果被提升为 double SELECT CASE WHEN 1 0 THEN 1 WHEN 2 0 THEN 2.0 ELSE 1.2 END; -- 1.0 -- 多条件通过 AND/OR 组合 SELECT id, CASE WHEN age 60 THEN senior WHEN age 30 AND age 60 THEN adult ELSE young-or-unknown END AS age_group FROM person; -- 在 GROUP BY 或 ORDER BY 中使用 CASE 做自定义分组/排序 SELECT CASE WHEN age IS NULL THEN unknown WHEN age 60 THEN senior ELSE adult END AS grp, COUNT(*) FROM person GROUP BY grp;执行层面从表达式到生成代码CaseWhen位于 sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/conditionalExpressions.scala属于ConditionalExpression条件表达式家族与If、CaseKeyWhen同源自 Spark 1.0.1 起引入。在生成执行代码codegen阶段Spark 对分支数量做了分策略优化doGenCode第 396-406 行单分支场景直接把CASE退化为If(cond, then, elseOrNull)并复用If的代码生成逻辑避免多余开销多分支场景使用multiBranchesCodegen第 301-394 行生成带resultState状态机的do { ... } while (false)代码块状态值-1未命中/0命中且非空/1命中但为空精确跟踪匹配进度一旦命中即continue跳出严格对应文档与eval中先匹配先返回的短路语义当分支数量庞大时还会通过splitExpressionsWithCurrentInputs将条件与取值拆分为多个子函数避免单个方法字节码超限。此外alwaysEvaluatedInputs第 243 行声明只有首个条件必然被求值而branchGroups第 249-271 行把所有条件与所有取值分别归组供公共子表达式消除等优化器规则识别跨分支共享的子表达式提升重复计算的复用率。测试侧sql/catalyst/src/test下的优化器测试如 RemoveRedundantAliasAndProjectSuite.scala也覆盖了CASE WHEN相关树的优化路径。小结Spark SQL 的CASE子句本质是CaseWhen条件表达式解析期由AstBuilder将简单式改写为等值比较、将搜索式直接映射为分支序列随后经类型校验、可空性推导与代码生成三阶段落地。使用时的三条核心纪律复杂范围判断,,AND/OR务必用搜索式CASE WHEN bool避免简单式引发的隐式类型转换陷阱见示例 2所有THEN与ELSE分支应保持类型一致Spark 会自动做安全强制转换否则抛DATA_DIFF_TYPES错误不写ELSE时全不匹配即返回NULL在WHERE/JOIN条件中使用时要留意 NULL 比较恒为未知/假的行为。相关语句SELECT MainWHERE ClauseGROUP BY ClauseHAVING ClauseORDER BY ClauseSORT BY ClauseDISTRIBUTE BY ClauseLIMIT ClauseOFFSET ClausePIVOT ClauseUNPIVOT ClauseLATERAL VIEW Clause赞分享大数据数据分析批处理流处理机器学习图计算【免费下载链接】sparkApache Spark - A unified analytics engine for large-scale data processing项目地址https://gitcode.com/gh_mirrors/sp/spark点击查看免费下载相关推荐AMD Ryzen处理器调试终极指南SMUDebugTool完整使用教程AMD Ryzen处理器调试终极指南SMUDebugTool完整使用教程 想要完全掌控你的AMD Ryzen处理器性能吗你是否遇到过游戏帧率不稳定、视频渲染数据库OLAP数据仓库大数据湖仓一体数据分析Apache Spark SQL 变量赋值语句 SET VAR 完全指南语法、规则与源码实现解析Apache Spark SQL 变量赋值语句 SET VAR 完全指南语法、规则与源码实现解析 导读 SET VAR 也可写作 SET VARIABLE大数据数据分析批处理流处理机器学习图计算Apache Spark SQL Scripting 之 CASE 语句简单与搜索式条件分支的完整指南Apache Spark SQL Scripting 之 CASE 语句简单与搜索式条件分支的完整指南 本篇技术指南聚焦 Apache Spark SQL S大数据数据分析批处理流处理机器学习图计算创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表