)
yq pivot 操作符实战用 yq 实现 RDBMS 风格的行列转置PIVOT【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq本文介绍 yq 内置的pivot操作符——一个对标主流 RDBMS如 SQL Server、OraclePIVOT函数的行列转置能力。它专门处理序列的序列与序列的映射两类数据形态是数据整理、报表预加工和 YAML/JSON 结构转换时的高频工具。读完本文你将掌握pivot的四种典型用法含缺失值补齐规则、它与 SQL PIVOT 的对应关系以及其底层实现原理与测试验证方式。pivot 是什么把行式数据旋转成列式数据在关系型数据库中PIVOT函数将行转换为列把每个实体一行、多个属性多列的表结构旋转为属性为行、实体为列的宽表结构。yq 的pivot操作符定义于 pkg/yqlib/operator_pivot.go在 YAML/JSON 数据上模拟了这一语义输入必须是序列sequence即顶层数据是一组元素数组元素的类型必须统一要么全部是序列要么全部是映射map输出与输入形成转置关系原本每个元素内部的第 N 个子项聚合为输出中第 N 个元素的子项序列。pivot 操作符的语法定义位于 pkg/yqlib/operation.govar pivotOpType operationType{Type: PIVOT, NumArgs: 0, Precedence: 52, Handler: pivotOperator, CheckForPostTraverse: true}由此可以确认两个重要事实pivot是零参数操作符NumArgs: 0直接写yq pivot即可使用同时它开启了PostTraverse后置遍历标记这意味着pivot[]这种带空方括号的写法会被解析为对当前文档中的每一个匹配节点分别执行 pivot对应的解析注册在 pkg/yqlib/lexer_participle.go 的simpleOp(pivot, pivotOpType)。在深入四种使用场景之前先明确 pivot 的基本调用形式。假设输入文件为sample.ymlyq pivot sample.yml # 或者通过标准输入 cat sample.yml | yq pivot场景一Pivot 序列的序列矩阵转置当输入是序列的序列即每个元素都是一个数组时pivot等价于数学上的矩阵转置把第 i 行第 j 列的元素移动到第 j 行第 i 列。给定sample.yml- - foo - bar - baz - - sis - boom - bah执行yq pivot sample.yml输出- - foo - sis - - bar - boom - - baz - bah从源码看这一分支由 pivotSequences 函数 实现它以map[int][]*CandidateNode按列下标收集每一行对应位置的元素随后按列下标顺序重建出新的序列。其效果与行数、列数是否相等无关这正是场景二要讨论的。场景二Pivot 异构序列——缺失值自动补 null实际数据往往并不规整每一行的长度可能不同。此时pivot并不会报错而是遵循缺失值被填充padded为 null的规则补齐。给定sample.yml第二行多了一个blah- - foo - bar - baz - - sis - boom - bah - blah执行yq pivot sample.yml输出- - foo - sis - - bar - boom - - baz - bah - - - blah注意最后一行第一个元素为空null第二个元素是blah。这正是缺失值补 null的直接体现。对应到源码pad 泛型函数 负责把短序列扩充到目标长度填充值由nullNodeFactorycreateScalarNode(nil, )生成——即一个值为 null 的标量节点pivotSequences在收集每列数据时append(pad(e, i, nullNodeFactory), row.Content[j])以及最终输出时pad(m[i], sz, nullNodeFactory)都应用了补齐逻辑保证输出矩阵是规整的矩形。场景三Pivot 序列的映射——行记录转列向量当输入是序列的映射即一个对象数组每行是一个 key-value 映射时pivot将每个键变为输出映射的一个键而该键对应的值变为这一键在所有记录中取值的序列。给定sample.yml- foo: a bar: b baz: c - foo: x bar: y baz: z执行yq pivot sample.yml输出foo: - a - x bar: - b - y baz: - c - z这正是对序列的序列场景的键名版本foo键下的[a, x]就是原数据第一列的转置结果。该分支由 pivotMaps 函数 实现它遍历每行映射的键值对for j : 0; j len(row.Content); j 2步长为 2 是因为 CandidateNode 中映射以键、值、键、值交错存储按键名分组收集值并保持键的首次出现顺序keys : make([]string, 0)仅在键第一次出现时追加因此输出的键顺序与输入中键首次出现的顺序一致。场景四Pivot 异构映射——缺失键同样补 null与场景二对称当各条记录的键不完全一致时某条记录缺少某个键则该键对应的值序列中相应位置会以 null 填充。给定sample.yml第二条记录多了what: ever且没有bar对应的差异可由实际数据灵活构造这里沿用官方示例——注意第二条记录同时包含额外键与全部原有键- foo: a bar: b baz: c - foo: x bar: y baz: z what: ever执行yq pivot sample.yml输出foo: - a - x bar: - b - y baz: - c - z what: - - ever可以看到新增的what键被追加到输出末尾其值序列中第一条记录的位置为 null。这一行为与pivotMaps中的补齐逻辑一致收集第 i 条记录的值时先pad(e, i, nullNodeFactory)最终输出时再pad(m[k], sz, nullNodeFactory)从而保证每个键的值序列长度都等于输入记录总数。pivot 的约束与错误边界pivot并非对任意数据都适用其前置校验逻辑集中在 pivotOperator 函数总结如下约束条件行为输入节点必须是序列!!seq否则报错cannot pivot node of type tag例如对单个标量或映射执行 pivot 会失败序列内所有元素的类型必须一致由getUniqueElementTag校验混合了!!seq与!!map会报错sequence contains elements of X and Y types元素类型只能是序列或映射其他类型如标量序列报错can only pivot elements of !!seq or !!map types序列长度不一致 / 映射键不一致不报错缺失位置自动填充 null另外值得注意的是pivot作用于每个匹配节点。结合CheckForPostTraverse: true你可以使用pivot[]对嵌套在文档中的每个数组分别做转置例如yq pivot[] sample.yml这一对每个匹配项执行 pivot的变体在 pkg/yqlib/operator_pivot_test.go 中有专门测试场景Pivot splat期望输出为按索引拆分后的逐列结果非常适合对多层级数据结构进行批量处理。与 SQL PIVOT 的对应关系为了帮助习惯关系型数据库的读者建立映射这里用一张对照表说明pivot与 SQLPIVOT的语义对应SQL PIVOT 概念yq pivot 对应透视列pivot column的取值序列元素的位置下标场景一/二或映射的键场景三/四聚合函数如 SUM/MAX无聚合直接收集原始值形成序列缺失组合结果为 NULL缺失位置填充 null输入表顶层序列数组核心差异在于SQL 的PIVOT通常需要指定聚合函数对值做归并而 yq 的pivot是纯结构变换——它不做任何聚合只是把数据从行优先重新组织为列优先值原样保留。测试验证官方行为即测试行为本文所有示例并非孤立的文档描述而是与单元测试一一对应。官方测试用例定义于 pkg/yqlib/operator_pivot_test.go通过expressionScenario表驱动结构覆盖了全部四个场景Pivot a sequence of sequencesL7-L13——对应场景一Pivot sequence of heterogeneous sequencesL25-L32——对应场景二副标题即注明Missing values are padded to null.Pivot sequence of mapsL34-L40——对应场景三Pivot sequence of heterogeneous mapsL42-L49——对应场景四同样以Missing values are padded to null.标注。测试中还包含Pivot splat场景验证pivot[]行为且该场景标记了skipDoc: true不进入文档生成流程说明pivot[]是文档之外、由实现额外支持的扩展能力。测试入口TestPivotOperatorScenarios位于同一文件 L52-L57你可以通过以下命令在本仓库中复现全部行为go test ./pkg/yqlib/ -run TestPivotOperatorScenarios -v小结何时该用 pivot一句话总结当你想把一组记录的纵向字段重排为字段为行、记录为列时就用pivot。典型收益场景包括将多行配置每行一个对象转置为按字段聚合的结构便于横向比较把 CSV/JSON 表格数据中的行式记录旋转为列向量供图表或模板直接消费对不规整数据长度不一的行、键不全的记录做规范化补齐得到规整的矩形结构。只需记住两条规则输入必须是元素类型统一的序列遇到缺失一律补 null。结合 operator_pivot.go 的源码与 operator_pivot_test.go 的测试你可以在自己的数据上快速验证并安全地使用这一操作符。【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考