ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

StarRocks JSON 比较运算符(<、<=、>、>=、=、!=)完整指南:语法、算术规则与底层实现

StarRocks JSON 比较运算符(<、<=、>、>=、=、!=)完整指南:语法、算术规则与底层实现 StarRocks JSON 比较运算符、、、、、!完整指南语法、算术规则与底层实现【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks本文系统讲解 StarRocks 提供的 JSON 比较运算符、、、、与!的用法覆盖操作数约束、类型转换规则、同类型与跨类型 JSON 值的比较次序并结合 BE 端源码揭示其底层比较实现。读完本文你将能够直接在 SQL 中对 JSON 类型的列进行过滤、排序与条件判断并理解 JSON 对象/数组比较结果背后的精确语义。一、概述JSON 比较运算符能做什么StarRocks 自 2.2.0 起支持 JSON 数据类型采用二进制格式编码存储 JSON 数据而非直接保存输入文本从而降低计算与查询时的解析开销。JSON 列可用于多维分析、实时分析等场景而 JSON 比较运算符正是对 JSON 数据执行查询过滤WHERE条件和排序判断ORDER BY的基础能力。StarRocks 支持以下 6 个 JSON 比较运算符运算符含义小于小于等于大于大于等于等于!不等于限制StarRocks不支持使用IN查询 JSON 数据这是 JSON 运算符与常规 SQL 类型的一个重要差异。关于全部 JSON 函数与运算符的索引可查阅 JSON 函数与运算符总览若希望在查询中使用普通列加速 JSON 过滤可结合生成列generated columns使用。二、操作数约束与隐式类型转换JSON 比较运算符对操作数有以下两条硬性约束运算符的两个操作数必须都是 JSON 值。如果其中一个操作数是 JSON 值而另一个不是则非 JSON 的操作数会在运算过程中被隐式转换为 JSON 值转换规则与CAST一致详见 cast 函数。在实际 SQL 中构造 JSON 操作数最常用的手段是PARSE_JSON(...)将字符串解析为 JSON 值参考 parse_jsonJSON_OBJECT(...)由键值对构造 JSON 对象参考 json_objectCAST(expr AS JSON)将 SQL 值显式转换为 JSON 值。隐式转换的典型场景是“JSON 值 vs. 数字/字符串字面量”例如PARSE_JSON(1) 1中右侧整数会被转换为 JSON NUMBER 后再参与比较。需要注意CAST仅支持兼容类型间的转换例如 JSON 字符串可转换为 SQL 字符串而 JSON 数字转换为 SQL 字符串会返回NULLSQL 值超出 JSON 精度范围时也会返回NULL以防溢出这些规则同样作用于隐式转换。三、同类型 JSON 值之间的比较算术规则一当运算符的两个操作数都是 JSON 值且数据类型相同时1. 基础数据类型NUMBER、STRING、BOOLEAN按该基础数据类型本身的算术规则进行比较。其中有一条值得注意的细化规则如果两个操作数都是数字但一个是 DOUBLE 而另一个是 INT则运算符会将 INT 值转换为 DOUBLE 值后再比较。也就是说JSON 数字比较不存在整数与浮点数混比的歧义PARSE_JSON(1) PARSE_JSON(1.0)成立。从底层实现看该行为对应 BE 端 json_value.cpp 中sliceCompare的逻辑当左右切片都属于同一 value type group即同为数字族时若两者 vpack 类型相同则 INT/SmallInt/UInt 走cmpInt64整数比较DOUBLE 走cmpDouble浮点比较基于std::isless/std::isgreater判定若一方是整数另一方是浮点数则统一走cmpDouble将数值转为 DOUBLE 比较——这正是文档所述“INT 提升为 DOUBLE”规则的实现体现。2. 复合数据类型OBJECT、ARRAY比较规则为以第一个操作数为基准将两个操作数中的键按字典序dictionary order排列然后逐键比较对应键的值。ARRAY 类型则按元素下标逐一比较对应位置的元素。因此复合类型的比较本质上是递归的逐层比较。示例 1键值不等时的比较第一个操作数为{a: 1, c: 2}第二个操作数为{b: 1, a: 2}。运算符首先比较键a的值第一个操作数中a为1第二个操作数中a为21 2因此判定第一个操作数小于第二个操作数mysql SELECT PARSE_JSON({a: 1, c: 2}) PARSE_JSON({b: 1, a: 2}); - 1示例 2键值相等时继续比较后续键第一个操作数为{a: 1, c: 2}第二个操作数为{b: 1, a: 1}。运算符先比较键a的值两边均为1、相等接着比较键c的值而第二个操作数中不存在键c因此判定第一个操作数大于第二个操作数mysql SELECT PARSE_JSON({a: 1, c: 2}) PARSE_JSON({b: 1, a: 1}); - 0这一语义在源码中体现得非常直接sliceCompare处理两个 Object 时用vpack::ObjectIterator(left)按第一个操作数的键顺序迭代通过right.get(key)在第二个操作数中查找同名键见 json_value.cpp若第二个操作数存在该键则递归比较两个值sliceCompare(it.value, sub)一旦某层比较结果非 0 立即返回若第二个操作数不存在该键sub.isNone()直接返回1表示第一个操作数更大全部键都比较完后返回left.length() - right.length()即键数量更多者更大。对于两个 ARRAY实现先比较长度left.length() - right.length()长度相等时按下标逐元素递归比较json_value.cpp完全对应“复合类型按序逐层比较”的规则。四、跨类型 JSON 值之间的比较算术规则二当运算符的两个操作数属于不同的 JSON 数据类型时StarRocks 按照固定的类型优先级次序进行比较NULL BOOLEAN ARRAY OBJECT DOUBLE INT STRING即JSON NULL 最小依次递增JSON STRING 最大。据此任意一个 JSON 字符串都大于任意一个 JSON 对象mysql SELECT PARSE_JSON(a) PARSE_JSON({a: 1, c: 2}); - 0该表达式中左侧是 STRING、右侧是 OBJECT按优先级 STRING OBJECT因此a {a: 1, c: 2}不成立返回0。对应到实现层当两个切片不属于同一 value type group 时sliceCompare会先特殊处理 vpack 的MinKey/MaxKey哨兵值然后直接以(int)left.type() - (int)right.type()的 vpack 类型序差值作为比较结果json_value.cpp。由于 vpack 为各 JSON 类型赋予了固定的枚举序这一差值天然与文档给出的优先级次序一致保证了跨类型比较结果可预测、可复现。五、底层实现JsonValue::compare 与调用链JSON 比较运算符在 BE 端的核心入口是JsonValue::compare系列方法定义于 be/src/types/json_value.h、实现于 be/src/types/json_value.cppint JsonValue::compare(const JsonValue rhs) const将左右两侧的二进制 JSON 分别转为 vpackSlice后调用静态函数sliceCompareint JsonValue::compare(const Slice lhs, const Slice rhs)供列式数据场景使用空 Slice 会映射为 vpack 的noneSlice()对应 JSON NULLint JsonValue::compare(const VSlice lhs, const VSlice rhs)直接基于 vpack 切片比较。其返回值语义与 C/C 惯例一致负数表示左小于右0表示相等正数表示左大于右恰好对应、、等运算符的判定。在表达式执行侧json_functions.cpp 中的JSON_CONTAINS等函数正是通过JsonValue::compare(target, candidate) 0来判断 JSON 值与候选值是否相等可见该比较实现同时支撑了运算符与 JSON 处理函数两条路径是 StarRocks 中所有 JSON 相等/次序判断的公共基石。六、实战在 WHERE 中使用 JSON 比较运算符结合 JSON 数据类型文档 中的建表与加载方式可以构造一个可运行的真实场景。首先创建含 JSON 列的表并写入数据CREATE TABLE tj ( id INT(11) NOT NULL COMMENT , j JSON NULL COMMENT ) ENGINEOLAP DUPLICATE KEY(id) COMMENT OLAP DISTRIBUTED BY HASH(id) PROPERTIES ( replication_num 3, storage_format DEFAULT ); INSERT INTO tj (id, j) VALUES (1, parse_json({a: 1, b: true})); INSERT INTO tj (id, j) VALUES (2, parse_json({a: 2, b: false})); INSERT INTO tj (id, j) VALUES (3, parse_json({a: 3, b: true})); INSERT INTO tj (id, j) VALUES (4, json_object(a, 4, b, false));随后即可用 JSON 比较运算符对j列执行过滤例如筛选出 JSON 值等于{a: 1, b: true}的记录SELECT * FROM tj WHERE j parse_json({a: 1, b: true});由于 JSON 对象比较时以第一个操作数为基准按键递归比较键顺序无关、字典序逐层比对上述等值判断可以正确处理键序不同的 JSON 对象。对存储了标量 JSON 的列同样可以直接使用大小比较例如筛选 JSON 数字大于某阈值的行SELECT * FROM tj_scalar WHERE j parse_json(10);使用建议两个操作数都显式构造为 JSON 值parse_json/json_object/CAST ... AS JSON避免依赖隐式转换带来额外开销等值比较时注意 OBJECT 会按键递归比较键越多比较开销越大对高频 JSON 过滤场景可将 JSON 中的常用字段抽出为普通列并配合生成列generated columns建立索引加速牢记IN不适用于 JSON 数据多值判断请改写为与OR的组合。七、小结规则维度结论支持运算符、、、、、!不支持IN操作数要求两侧必须均为 JSON 值非 JSON 操作数隐式转换为 JSON规则同 CAST同类型基础值按基础类型规则比较INT 与 DOUBLE 混比时 INT 提升为 DOUBLE同类型复合值OBJECT 按第一个操作数的键序逐键递归比较ARRAY 先比长度再逐元素比较跨类型比较固定优先级NULL BOOLEAN ARRAY OBJECT DOUBLE INT STRING底层实现JsonValue::compare vpacksliceCompare见 json_value.cppJSON 比较运算符为半结构化数据的过滤与排序提供了统一、可预期的语义无论 JSON 值是标量还是嵌套对象/数组比较结果都严格遵循类型优先级与逐层递归规则。理解这些规则后你可以在 WHERE 条件中安全地使用、!、等运算符处理 JSON 列也能在阅读查询结果时准确预判对象间的大小关系。【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表