简介这份资源面向使用KettlePentaho Data Integration进行数据集成开发的工程师聚焦「循环获取结果集并传入转换」这一典型场景帮助解决跨转换传递变量、按行迭代处理数据的实际问题。资源包共1个文件为PDF格式大小约130KB内容围绕t1.ktr与var.ktr两个转换的配合展开涵盖JavaScript步骤中previous_result.getRows()获取结果集、parent_job.setVariable设置tables、size、i、id、name等变量以及循环控制与文本文件输出的完整思路。目前已有3111人学习下载适合具备一定Kettle基础、希望掌握作业与转换间变量传递与循环处理技巧的读者参考可据此理解结果集迭代、变量更新及最终输出到本地txt文件的实现方式为自定义数据处理流程提供可复用的排错与设计参考。1. Kettle 循环取结果集再喂给转换一条被低估的数据管道你有没有遇到过这种局面上游一个查询吐出几百行配置数据每一行都代表一组参数需要拿这组参数去跑同一个转换逻辑跑完再把结果写回目标表。很多人第一反应是在作业里挂一个「转换」再套一个「转换」结果发现 Kettle 的转换之间根本不共享结果集上一棒的数据下一棒拿不到整个链路直接断掉。这不是配置写错了而是没搞懂 Kettle 里「结果集」和「转换」的边界在哪里。这篇要讲的就是把这件事做通用 Kettle 循环获取结果集中的数据并逐行传入转换里面执行。核心手段是「获取系统信息」拿到上一步的行数配合「JavaScript 脚本」维护一个自增游标再用「从结果获取字段」把当前行的值映射成变量最后用「执行转换」把变量带进子转换。适合已经会用 Spoon 画基本转换、但一碰到跨转换传参就卡住的同学。下面按「先立住原理、再动手复现、最后讲坑」的顺序推下去。2. 结果集、变量与执行转换Kettle 跨转换传参的三层机制2.1 为什么转换之间默认不共享数据Kettle 的转换Transformation是数据流的最小执行单元它内部靠跳Hop在步骤之间传递行但转换一旦结束这些行就随内存释放掉了。作业Job是调度单元它管的是「先跑 A 再跑 B」的顺序不负责把 A 的行数据搬到 B。所以你在作业里串两个转换第二个转换的输入步骤是空的这不是 bug是设计如此。那结果集Result是什么它是作业层面能拿到的一层薄数据上一个转换执行完Kettle 会把它的「结果」登记到作业上下文里包括写入行数、读取行数、错误行数以及一个叫「结果文件」的东西。注意默认情况下结果集里并不包含你查询出来的那些业务字段除非你显式把行复制到结果Copy rows to result。这一步是整条链路能不能通的关键开关。理解了这层你就明白为什么标题里强调「循环获取结果集中的数据」——数据得先被登记进结果集循环才有东西可拿。2.2 循环的三件套行数、游标、取值Kettle 没有原生的 for-each 步骤循环是靠「计数器 条件判断 变量」拼出来的。常见做法是三步第一步用「获取系统信息」步骤勾选「结果里的行数」它会输出一个字段值是上一步结果集的总行数。第二步用一个「JavaScript 脚本」步骤维护游标变量初始为 0每次循环加 1并判断是否小于总行数。第三步用「从结果获取字段」步骤按游标位置把结果集里第 N 行的某个字段取出来赋给一个变量。这三步串起来就形成了一个「知道有多少行、知道现在第几行、能把第几行的值拿出来」的闭环。缺任何一环循环要么跑飞要么取到空值。2.3 执行转换步骤怎么把变量带进去「执行转换」步骤Transformation Executor是作业里的一个条目它允许你指定一个子转换文件并且可以传入「参数」或「变量」。这里有个容易混的点参数Parameter和变量Variable在 Kettle 里是两套东西。参数是子转换定义时声明的作用域固定在子转换内部变量是全局或父级设置的子转换里用${变量名}引用。实操中更稳的做法是用变量。父级用「设置变量」步骤把当前行的值写成变量子转换里用${变量名}读取。这样你不用改子转换的参数声明复用性更好。代价是变量是全局的多个循环并发时会互相覆盖所以循环必须串行执行不能并行。提示如果你的子转换需要同时接收多个字段就在父级连续设置多个变量子转换里逐个引用不要试图把一整行塞进一个变量。2.4 一张表看清三种传参方式的边界方式作用域是否支持循环逐行典型坑复制行到结果作业级结果集支持需配合游标忘记勾选导致结果集为空参数子转换内部支持但需预声明参数名拼错不报错值为空变量全局/父级支持推荐并发时互相覆盖选型建议字段少、循环串行用变量字段多、需要类型校验用参数只是传递行数或简单标记用结果集自带的统计值。3. 从零搭一条可复现的循环传参链路3.1 准备一张配置表和目标表先造数据不然没法验证。假设有一张cfg_task表每行代表一个待处理的任务参数-- 配置表每行是一组要传入子转换的参数 CREATE TABLE cfg_task ( task_id INT PRIMARY KEY, task_name VARCHAR(50), batch_no INT ); INSERT INTO cfg_task VALUES (1, sync_user, 100); INSERT INTO cfg_task VALUES (2, sync_order, 200); INSERT INTO cfg_task VALUES (3, sync_log, 300);目标表task_result用来接子转换写回的结果字段随意能看出哪行跑过就行。这两张表用同一个数据库连接后面父转换查cfg_task子转换写task_result。3.2 父转换查询 复制行到结果新建一个转换命名parent_loop。步骤顺序「表输入」步骤SQL 写SELECT task_id, task_name, batch_no FROM cfg_task ORDER BY task_id。排序很重要结果集是按顺序取的不排序游标和行会对不上。「复制记录到结果」步骤Copy rows to result把上一步的字段全部复制进结果集。这两个步骤用跳连起来保存。此时如果你直接跑这个转换它只会把 3 行数据塞进结果集然后结束。结果集在作业上下文里还活着等作业下一步来取。注意ORDER BY不能省。我见过有人不排序数据库返回顺序变了游标取到的行和预期完全错位排查半天以为是变量没传进去。3.3 作业拿行数、开循环、执行转换新建作业job_loop条目顺序如下「转换」条目指向parent_loop。「获取系统信息」条目勾选「结果里的行数」输出字段命名total_rows。「JavaScript」条目写游标逻辑。「执行转换」条目指向子转换child_process。JavaScript 条目的代码// 初始化游标只在第一次执行时置 0 if (typeof parent_job.getVariable(cursor) undefined || parent_job.getVariable(cursor) null) { parent_job.setVariable(cursor, 0); } var cursor parseInt(parent_job.getVariable(cursor)); var total parseInt(parent_job.getVariable(total_rows)); // 游标未越界则自增越界则保持不变 if (cursor total) { cursor cursor 1; parent_job.setVariable(cursor, cursor.toString()); parent_job.setVariable(has_more, Y); } else { parent_job.setVariable(has_more, N); }逻辑说明cursor从 0 开始每次进这个脚本先判断有没有初始化没有就置 0。然后读总行数和当前游标只要游标小于总行数就加 1并把has_more置为Y。参数说明total_rows来自上一步「获取系统信息」名字必须和那里输出的一致cursor和has_more是自定义变量后面步骤引用。3.4 用条件判断把循环闭起来光有游标还不够得让作业知道什么时候停。在 JavaScript 条目后面加一个「条件」条目Simple Evaluation判断has_more是否等于Y为真走「执行转换」条目跑完再跳回 JavaScript 条目形成回环。为假走「成功」结束。这个回环就是 Kettle 里最朴素的循环。注意跳转方向别画反画反了要么死循环要么一次都不跑。3.5 子转换用变量取当前行并写回子转换child_process里用「获取变量」步骤读${cursor}再用「表输入」按游标查当前行SELECT task_id, task_name, batch_no FROM cfg_task WHERE task_id ( SELECT task_id FROM cfg_task ORDER BY task_id LIMIT 1 OFFSET ${cursor} - 1 )这里用OFFSET ${cursor} - 1是因为游标从 1 开始而 OFFSET 从 0 开始。查出来的字段再走一个「表输出」写进task_result。这样每循环一次子转换就处理一行父转换的游标推进一步直到has_more变N。提示子转换里引用变量用${cursor}不要写成$cursor或{cursor}Kettle 只认前者。变量名大小写敏感父级设的是cursor子级就不能写Cursor。4. 避坑与排查循环传参最容易翻车的五个点4.1 结果集为空循环一次都不跑现象作业跑完子转换没执行日志里total_rows是 0。 原因父转换里忘了加「复制记录到结果」步骤或者加了但没连在表输入后面。 解决打开父转换确认「复制记录到结果」存在且跳线正确单独跑一次父转换看日志里有没有「Copy rows to result」的行数输出。4.2 游标不递增死循环现象作业一直跑日志刷屏cursor始终是 1。 原因JavaScript 里用了var cursor局部变量没写回parent_job.setVariable下次进来又从头读。 解决所有要跨步骤保留的值必须用parent_job.setVariable写回不能只存在脚本局部变量里。4.3 变量传进子转换是空值现象子转换里${cursor}解析出来是空字符串SQL 报错或查不到数据。 原因变量名拼写不一致或者「设置变量」步骤的作用域设成了「当前作业」而不是「父作业」。 解决在子转换开头加一个「写日志」步骤把${cursor}打出来看确认父级设置变量时作用域选对子转换引用时大小写一致。4.4 循环顺序和结果集顺序对不上现象处理的行顺序乱了或者某行被处理两次。 原因父转换的 SQL 没加ORDER BY数据库返回顺序不稳定。 解决父转换 SQL 强制ORDER BY主键子转换取行时也用同样的排序两边顺序必须一致。4.5 子转换报「表不存在」但表明明在现象子转换单独跑没问题被作业调用就报表不存在。 原因子转换用的数据库连接和父转换不是同一个或者连接在子转换里没被正确继承。 解决子转换里显式配置数据库连接不要依赖父级传递确认连接名和父转换一致测试时先单独跑子转换。5. 把循环传参做稳的三个进阶习惯第一个习惯用「写日志」代替猜。循环类作业最难的是看不见中间状态我一般会在 JavaScript 后面加一个「写日志」条目把cursor、total_rows、has_more三个值打出来。跑一次看日志比盯着 Spoon 界面猜快十倍。日志级别用Basic就够别开Debug否则刷屏。第二个习惯给循环加一个硬上限。万一逻辑写错导致死循环作业会一直跑下去。在 JavaScript 里加一句如果cursor超过total_rows的 1.5 倍强制把has_more置N并写一条错误日志。这是后悔药平时用不上出事时能救你。第三个习惯子转换尽量无状态。子转换里不要依赖上一次循环留下的变量每次进来都从${cursor}重新取数。这样即使循环中断重跑也不会因为残留状态导致数据错乱。验证方法很简单跑完作业后查task_result的行数是否等于cfg_task的行数再抽查几行的task_id是否和配置表一一对应。对不上就回到日志里看游标停在哪一步。我自己的教训是早期做这类循环时总想省掉「复制记录到结果」觉得表输入查出来直接就能用结果每次都在结果集为空上卡半天。后来养成习惯只要涉及跨转换传参第一步先确认结果集有没有数据再往下写。希望帮到你。本文还有配套的精品资源点击获取
