Kettle是一款国外开源的ETL工具,纯java编写,可以在Windows、Linux、Unix上运行,数据抽取高效稳定。本人用的版本是6.1。

 

之前用kettle这个开源的ETL工具是用来做数据抽取的,这次测试有个需求,是做数据库数据的比对。验证源表到目标表的逻辑正确,从而利用源表自己写SQL逻辑,生成的结果和目标表去比对。这次想到了利用kettle的数据比较功能,来做这些数据校验。

 

kettle 从mysql 读取变量 kettle数据_kettle

这个任务主要是读取输入参数CSV里面的每行数据,填充到SQL的参数里面,然后执行两个SQL的比对,最后统计测试结果。

 

下面来看每一步的操作:

【清空上次结果】

kettle 从mysql 读取变量 kettle数据_测试_02

如图,右键节点可以打开对应的Transformation。

kettle 从mysql 读取变量 kettle数据_kettle_03

kettle 从mysql 读取变量 kettle数据_kettle_04

public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException{



    String resultFoldPath = getParameter("resultPath");

    process(resultFoldPath);

    return false;

}  

   

    public void process(String resultFoldPath) {

        //清空结果文件

       

        File tempFile = new File(resultFoldPath);

    File[] listFiles = tempFile.listFiles();

    for(int i=0;i<listFiles.length;i++){

            listFiles[i].delete();         

        

    }

    }

 

这是一个“Java代码”组件,主要清空指定目录的所有文件。

 

【读取数据】

kettle 从mysql 读取变量 kettle数据_kettle_05

kettle 从mysql 读取变量 kettle数据_java_06

利用“CSV文件输入”组件,读取username和rownum的字段值,可以读取多行。

 

【初始化变量】

kettle 从mysql 读取变量 kettle数据_kettle 从mysql 读取变量_07

这是任务里面的一个JS组件,用于把上一步骤里面读取到的数据通过previous_result.getRows()取出来,然后放到tables里面。注意这里放到tables里面以后格式变成了[[a1],[b1],[a2],[b2]]。Size是tables的总行数。这里的i是定义的循环行的index,初始值是0。最后我们把第一行的所有字段都取出来,放到同名的变量里面。

var prevRow = previous_result.getRows();

if(prevRow == null ||(prevRow.size() == 0)){

    false;

}else{

    parent_job.setVariable("tables",prevRow);//ArrayList存储表名变量,以数组形式保存入[[table1],[table2]]

    parent_job.setVariable("size",prevRow.size());//存储执行表的总数量

    parent_job.setVariable("i",0)//循环控制变量



    for(k=0;k<prevRow.get(0).size();k++){

        //获取每列的列名

        var name = prevRow.get(0).getRowMeta().getFieldNames()[k];

        //parent_job.setVariable("name",name); name变量可以输出到日志用于调试

        var value = prevRow.get(0).getString(k,"");

        //parent_job.setVariable("value",value); value变量可以输出到日志用于调试

        parent_job.setVariable(name,value);

    }



    true;

}

网上给的例子一般都是写死的,比如给name变量赋值: parent_job.setVariable(“name”,value),但是本项目的参数是需要经常变化的,为了减少维护量,特意去查询了kettle的源码(源码地址:https://github.com/pentaho/pentaho-kettle,选择6.1版本下载)。这里getRows()返回的结果是org.pentaho.di.core.RowMetaAndData类型的数组,可以调用RowMetaAndData里的方法来取值或进行其他操作,这里仅取了列名和列值。

 

【循环控制器】

kettle 从mysql 读取变量 kettle数据_测试_08

“循环控制器”实际是一个“校验字段值”组件,可以进行循环的条件是i<size,从而保证可以循环执行。

 

【执行SQL】

kettle 从mysql 读取变量 kettle数据_kettle 从mysql 读取变量_09

kettle 从mysql 读取变量 kettle数据_数据_10

这里设置了两组比较。

kettle 从mysql 读取变量 kettle数据_java_11

读取源表数据的操作,这里在SQL里把需要替换的变量用${}加进来,“替换SQL语句里的变量”选项要打勾。同理读取目标表数据。

kettle 从mysql 读取变量 kettle数据_kettle_12

数据比对用的是“合并记录”的组件,匹配的关键字是指用于定位两个数据源中的同一条记录。数据字段是指对于两个数据源中的同一条记录中,指定需要比较的字段。最后生成的数据将包括旧数据来源和新数据来源里的所有数据,对于变化的数据,使用新数据代替旧数据,同时在结果里用一个标志字段,来指定新旧数据的比较结果。“identical” – 旧数据和新数据一样;“changed” – 数据发生了变化; “new” – 新数据中有而旧数据中没有的记录;“deleted” –旧数据中有而新数据中没有的记录。

kettle 从mysql 读取变量 kettle数据_数据_13

同理再建一个反向的数据比对,把旧数据源和新数据源互换下。

kettle 从mysql 读取变量 kettle数据_kettle 从mysql 读取变量_14

最后把结果输出到文本文件,选择扩展名是CSV,输出的文件名中可以添加变量i或者其他的变量,最后的文件名是-{源表名}-{目标表名}-正/反,最后的正反的命名取决于正向比对还是反向比对。

 

要建正向和反向比对的原因是因为测试人员要求输出的结果必须包含有两张表的数据和结果,如果要重新写个组件花费的时间比较长,最终讨论以后采用折中的策略,通过多加一步比对来实现。

 

【条件变化】

kettle 从mysql 读取变量 kettle数据_数据_15

var prevRow=previous_result.getRows();

var list = parent_job.getVariable("tables").replace("[","").replace("]","").split(",");

var size = new Number(parent_job.getVariable("size"));

var i = new Number(parent_job.getVariable("i"))+1;



if(i<size){

    var length = prevRow.get(0).size();//列宽

    for(k=0;k<length;k++){

        var name = prevRow.get(i).getRowMeta().getFieldNames()[k];

        //parent_job.setVariable("name",name);



        var value = list[length*i+k].replace(" ","");

        //parent_job.setVariable("value",value);

        parent_job.setVariable(name,value);   



    }



}

parent_job.setVariable("i",i);

true;

这个操作是当一次执行完成后,要跳到下一个执行的操作。索引i要加1。获取下一行的数据,网上很多给的例子都是用prevRow.get(i)来获取下一行,但是在实际用的时候发现每次获取的都是第一行,想了很多办法,最后还是通过处理tables来获取下一行,用parent_job.getVariable("tables").replace("[","").replace("]","").split(",")将其转化成数组,然后通过list[length*i+k].replace(" ","")来获取下一行的每个列值。

 

【统计测试结果】

kettle 从mysql 读取变量 kettle数据_java_16

统计测试结果也是一个“Java代码”组件,这里吐槽下,直接把代码贴到文本框里面是有问题的,像List、JSON等一些复杂结构是无法支持的,因此在发现IDE里面的可正确运行的代码在kettle报错以后,就采用了Jar插件的方法。把java代码导出为Jar放入到kettle的lib文件夹下,然后重启spoon,这样就可以import外部类了。

 

kettle 从mysql 读取变量 kettle数据_java_17

最后生成的结果如上图所示。

kettle 从mysql 读取变量 kettle数据_kettle 从mysql 读取变量_18

kettle 从mysql 读取变量 kettle数据_kettle 从mysql 读取变量_19

上图是每个详细结果文件和测试结果汇总文件。

有兴趣的可以下载本人的DEMO,包含ktr、job、jar文件。