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

资讯详情

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

Apache Druid NULL 值处理实战指南:SQL 兼容模式下的字符串与数值列查询

Apache Druid NULL 值处理实战指南:SQL 兼容模式下的字符串与数值列查询 数据库OLAP大数据后端【免费下载链接】druidApache Druid: a high performance real-time analytics database.项目地址https://gitcode.com/gh_mirrors/druid6/druid点击查看免费下载本文是一份面向 Druid 用户的 NULL 值处理实操教程。文章围绕 Apache Druid 默认的 ANSI SQL 兼容 NULL 语义通过一份同时包含空字符串、字符串 NULL、数值 NULL 的样例数据逐步演示字符串列过滤、数值列比较、聚合计数与摄入期ingestion time过滤中的 NULL 行为差异并深入仓库源码级实现说明 NULL 在段segment内如何存储、三个相关运行时配置如何控制三值逻辑以及从旧版默认值模式迁移到 SQL 兼容模式的三种改造路径。读完本文你将掌握在 Druid SQL 中正确编写 NULL 相关过滤、计数与算术表达式的能力避免NULL 不计入结果、NULL 不等于 0这类典型陷阱。前置条件开始本教程前请先按 Local quickstart 在本地下载并运行 Apache Druid。教程假设你已经熟悉使用 Query view 来摄取和查询数据并且没有修改任何与 NULL 处理相关的默认配置即保持druid.generic.useDefaultValueForNullfalse、druid.expressions.useStrictBooleanstrue、druid.generic.useThreeValueLogicForNativeFilterstrue的默认状态。加载包含 NULL 值的示例数据样例数据同时包含字符串列与数值列的 NULL 值{date: 1/1/2024 1:02:00,title: example_1,string_value: some_value,numeric_value: 1} {date: 1/1/2024 1:03:00,title: example_2,string_value: another_value,numeric_value: 2} {date: 1/1/2024 1:04:00,title: example_3,string_value: , numeric_value: null} {date: 1/1/2024 1:05:00,title: example_4,string_value: null, numeric_value: null}注意四行数据的差异example_3的string_value是空字符串numeric_value为nullexample_4的string_value与numeric_value均为null。在 Druid 控制台的Query视图中运行下面的 SQL通过 MSQ 任务引擎REPLACE INTO ... OVERWRITE ALL语法把数据写入null_example数据源REPLACE INTO null_example OVERWRITE ALL WITH ext AS ( SELECT * FROM TABLE( EXTERN( {type:inline,data:{\date\: \1/1/2024 1:02:00\,\title\: \example_1\,\string_value\: \some_value\,\numeric_value\: 1}\n{\date\: \1/1/2024 1:03:00\,\title\: \example_2\,\string_value\: \another_value\,\numeric_value\: 2}\n{\date\: \1/1/2024 1:04:00\,\title\: \example_3\,\string_value\: \\, \numeric_value\: null}\n{\date\: \1/1/2024 1:05:00\,\title\: \example_4\,\string_value\: null, \numeric_value\: null}}, {type:json} ) ) EXTEND (date VARCHAR, title VARCHAR, string_value VARCHAR, numeric_value BIGINT) ) SELECT TIME_PARSE(date, d/M/yyyy H:mm:ss) AS __time, title, string_value, numeric_value FROM ext PARTITIONED BY DAY这里用EXTERN定义内联 JSON 输入用EXTEND显式声明列类型numeric_value声明为BIGINT用TIME_PARSE(date, d/M/yyyy H:mm:ss)把原始日期字符串解析为__time时间戳并按天分区。数据加载完成后查询整张表SELECT * FROM null_exampleDruid 返回__timetitlestring_valuenumeric_value2024-01-01T01:02:00.000Zexample_1some_value12024-01-01T01:03:00.000Zexample_2another_value22024-01-01T01:04:00.000Zexample_3emptynull2024-01-01T01:05:00.000Zexample_4nullnull请特别留意example_3的空字符串值与example_4的 NULL 字符串值之间的区别在 SQL 兼容模式下Druid 会分别展示为empty与null两种不同的值。字符串列的 NULL 处理本节的查询演示字符串列上的 NULL 处理方式。下面这条查询过滤掉string_value不等于some_value的行SELECT COUNT(*) FROM null_example WHERE string_value ! some_valueDruid 返回2只统计了another_value和空字符串两行。NULL 值不计入结果——按照 ANSI SQL 的三值逻辑NULL ! some_value的结果是unknown既不成立也不成立因此被过滤掉。这正是 SQL 兼容模式与旧版默认值模式最显著的行为差异旧模式中 NULL 与空字符串等价会被计入。再来看聚合计数的差异。COUNT(*)统计全部行数而COUNT(string_value)只统计该列非 NULL 的行数SELECT string_value, COUNT(*) AS count_all_rows, COUNT(string_value) AS count_values FROM null_example GROUP BY 1Druid 返回string_valuecount_all_rowscount_valuesnull10empty11another_value11some_value11可以看到NULL 行虽然计入COUNT(*)但在COUNT(string_value)中计为 0。同时注意GROUP BY 会把null和空字符串作为两个不同的分组值各自独立成行——这与旧版模式中空串与 NULL 互换的行为完全不同。同时过滤空字符串与 NULL如果你的业务逻辑需要把空字符串和 NULL 等同看待可以在过滤条件中使用 OR 运算符。例如选出所有字符串列为 NULL 或空字符串的行SELECT * FROM null_example WHERE string_value IS NULL OR string_value Druid 返回__timetitlestring_valuenumeric_value2024-01-01T01:04:00.000Zexample_3emptynull2024-01-01T01:05:00.000Zexample_4nullnull再举一个例子如果不想统计空字符串可以用FILTER子句把空串排除掉SELECT COUNT(string_value) FILTER(WHERE string_value ) FROM null_exampleDruid 返回2。空字符串和 NULL 都被排除NULL 同样为unknown只剩some_value与another_value两行被计数。数值列的 NULL 处理Druid不会把 NULL 计入数值比较。例如SELECT COUNT(*) FROM null_example WHERE numeric_value 2Druid 返回1。虽然example_3的数值列在摄取时写入了 NULL但它既不满足 2也不满足 2因此example_3与example_4都被排除只有example_1数值 1命中。此外必须注意NULL 数值不会表现得像 0 一样参与算术运算。例如SELECT numeric_value 1 FROM null_example WHERE __time 2024-01-01 01:04:00.000Z该查询命中的是example_4行其numeric_value为 NULL因此 Druid 返回null而不是 1——按照 ANSI SQL 标准NULL 1的结果仍是 NULL。一种解决方式是使用COALESCE函数对 NULL 做兜底处理SELECT COALESCE(numeric_value, 0) 1 FROM null_example WHERE __time 2024-01-01 01:04:00.000Z这次 Druid 返回1因为COALESCE把 NULL 替换成了 0。COALESCE在 Druid SQL 标量函数中的定义是返回第一个既不是 NULL 也不是空字符串的值是处理数值 NULL 最常用的函数之一。摄入期ingestion time过滤NULL 处理规则同样适用于摄入阶段。SQL 兼容模式下MSQ 的REPLACE INTO ... SELECT中的WHERE子句在写段之前就会按相同语义过滤数据。下面这条查询用带WHERE的 SELECT 替换示例数据REPLACE INTO null_example OVERWRITE ALL WITH ext AS ( SELECT * FROM TABLE( EXTERN( {type:inline,data:{\date\: \1/1/2024 1:02:00\,\title\: \example_1\,\string_value\: \some_value\,\numeric_value\: 1}\n{\date\: \1/1/2024 1:03:00\,\title\: \example_2\,\string_value\: \another_value\,\numeric_value\: 2}\n{\date\: \1/1/2024 1:04:00\,\title\: \example_3\,\string_value\: \\, \numeric_value\: null}\n{\date\: \1/1/2024 1:05:00\,\title\: \example_4\,\string_value\: null, \numeric_value\: null}}, {type:json} ) ) EXTEND (date VARCHAR, title VARCHAR, string_value VARCHAR, numeric_value BIGINT) ) SELECT TIME_PARSE(date, d/M/yyyy H:mm:ss) AS __time, title, string_value, numeric_value FROM ext WHERE string_value ! some_value PARTITIONED BY DAY重跑SELECT * FROM null_example结果只剩两行__timetitlestring_valuenumeric_value2024-01-01T01:03:00.000Zexample_2another_value22024-01-01T01:04:00.000Zexample_3emptynullexample_1some_value被过滤掉example_4字符串为 NULL也因NULL ! some_value为 unknown 而被过滤——在摄入期过滤中NULL 同样不会被当作不等于某值而保留。这意味着如果你在摄入时依赖!过滤做数据清洗需要注意 NULL 行会被一并丢弃。底层原理Druid 如何存储与判断 NULL三值逻辑与三个运行时配置Druid 默认的 NULL 处理对标 ANSI SQL 的三值逻辑true / false / unknown任何与 NULL 的比较、!、等结果都是 unknown既不会命中也不会被!保留。这一行为由三个运行时属性共同控制见 docs/configuration/index.md 与 SQL 数据类型文档配置项作用默认值druid.generic.useDefaultValueForNull设为false时以 SQL 兼容模式存储和查询数据设为true进入已废弃的旧版模式NULL 在字符串列存为、在数值列存为0。该配置已废弃未来版本将固定为false行为falsedruid.expressions.useStrictBooleans控制表达式布尔逻辑模式为true时所有表达式布尔值统一用 1真/ 0假表示truedruid.generic.useThreeValueLogicForNativeFilters控制原生native过滤器是否使用 SQL 兼容的三值逻辑需在useDefaultValueForNullfalse且useStrictBooleanstrue时生效设为false时过滤器回退为两值逻辑。该配置已废弃未来将固定为true行为true这三个配置相互配合只要其中一个被改成非默认值非表达式型过滤器就会回退为两值逻辑表达式型过滤器则单独由useStrictBooleans控制。从 docs/release-info/migr-ansi-sql-null.md 可以看到Druid 28.0.0 起默认即启用以上三个值它们均已标记为 deprecated 并计划在未来版本中移除届时 Druid 将始终采用 SQL 兼容行为。NULL 在段内的存储结构从段存储层面看段设计与 NULL 处理 一节说明默认 SQL 兼容模式下字符串列的 NULL 在值字典dictionary中固定占用 id 0字典首位并配合位图bitmap值索引用于过滤 NULL数值列则维护一张NULL 行位图用于聚合时的 NULL 检查与过滤器对 NULL 值的匹配。这就是COUNT(string_value)能区分 NULL 行、GROUP BY能把null单独分组、数值比较能跳过 NULL 行的物理基础。而在旧版 legacy 模式下druid.generic.useDefaultValueForNulltrueDruid 28.0.0 之前的默认行为摄入时写入的段有以下特征字符串列无法区分与null两者被当作同一个值互换处理数值列无法表示 NULL 行统一存储为0。由于 legacy 模式的数值列没有 NULL 行位图段体积可能略小某些涉及数值列的查询也略微更快无需检查 NULL 位图但代价是丢失了标准 SQL 语义。从旧版模式迁移到 SQL 兼容模式的三种策略如果你的业务逻辑依赖旧版默认值模式的行为可以按以下三种方式之一改造完整示例见 SQL 兼容模式迁移指南策略一摄入时把 NULL 替换为默认值不保留 NULL如果不关心是否保留 NULL可以在摄入 SQL 的 SELECT 中用COALESCE/NVL把字符串 NULL 换成、数值 NULL 换成0使现有查询在 SQL 兼容模式下表现得与旧版一致SELECT TIME_PARSE(time) AS __time, COALESCE(string_example, ) AS string_example, NVL(number_example, 0) AS number_example FROM extNVL的定义是若 value1 非 NULL 则返回 value1否则返回 value2见 docs/querying/sql-scalar.md与COALESCE在二参数场景下等价。策略二摄入时把空字符串转成 NULL依赖 IS NULL 语义如果旧查询依赖col IS NULL命中空串legacy 模式两者等价可在摄入时用NULLIF(string_example, )把空串统一转为 NULL这样IS NULL过滤依然能命中原来的空串数据。策略三保留 NULL改写查询为 ANSI SQL 兼容如果需要保留数据中的 NULL则必须改写客户端查询典型手法包括不等值过滤显式带上 NULLx some_value改写为(x some_value OR x IS NULL)用空值安全比较IS DISTINCT FROM达到包含 NULL的不等判断string_value IS DISTINCT FROM some_value算术运算前用COALESCE/NVL兜底NVL(number_example, 0) 1。扩展阅读Null valuesSQL 数据类型文档Druid 对 NULL 值的完整行为描述包括布尔三值逻辑的三种控制配置。SQL 兼容模式迁移指南从 legacy 模式迁移的详细步骤与完整 SQL / JSON 摄取示例。Handling null values段设计文档NULL 在段内的存储结构字典 id 0、NULL 行位图与 legacy 模式的段特征。Druid SQL 标量函数COALESCE、NVL、NULLIF等 NULL 处理函数的完整定义。赞分享数据库OLAP大数据后端【免费下载链接】druidApache Druid: a high performance real-time analytics database.项目地址https://gitcode.com/gh_mirrors/druid6/druid点击查看免费下载相关推荐Apache Druid SQL 数据类型完全指南标准类型、数组、多值字符串与 NULL 处理Apache Druid SQL 数据类型完全指南标准类型、数组、多值字符串与 NULL 处理 Druid 为每一列关联一个特定的数据类型而 Druid S数据库OLAP大数据后端Apache Druid ANSI SQL 兼容模式迁移指南从遗留 null 处理到 SQL 三值逻辑Apache Druid ANSI SQL 兼容模式迁移指南从遗留 null 处理到 SQL 三值逻辑 Apache Druid 自 28.0.0 起默认的数据库OLAP大数据后端Apache Druid SQL 多值字符串函数MV_*完全指南从 MV_FILTER_ONLY 到 MV_TO_ARRAYApache Druid SQL 多值字符串函数MV_ 完全指南从 MV_FILTER_ONLY 到 MV_TO_ARRAY 本文以 Apache Dru数据库OLAP大数据后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表