無意中在網(wǎng)上看到一篇文章《利用kettle中的JS來完成ETL數(shù)據(jù)校驗》,挺受啟發(fā)的,覺得用JS來實現(xiàn)ETL的自動化校驗,是個不錯的思路。但是這篇文章里給的JS腳本樣例確實有待改進,一是讓初學(xué)者看著不那么清晰,二是擴展性和維護性都較差。于是我做了二次改造,重新編寫了腳本,如下:
//Script here
var strConn = "TestOrcl";
var check_status=0;//如果檢測到有任何一種校驗錯誤,則check_status=1
var check_table="E_OT_PRMTALT";//校驗有關(guān)的表名字
var check_table_id="LICALTID";//校驗表主鍵
var check_name=LICALTID;//校驗表主鍵值
var check_detail="";//校驗到的錯誤詳細情況
var check_type="";//校驗到的錯誤類型
var check_date=new Date();//校驗時間
var source_table="E_OT_PRMTALT";//數(shù)據(jù)源表名稱,如果數(shù)據(jù)來自多個數(shù)據(jù)表,則需要聲明多個
var source_table_id="LICALTID";//數(shù)據(jù)源表主鍵,如果多個表聯(lián)合主鍵,則需要聲明多個主鍵
/////////////////////////////字段唯一性校驗//////////////////////////////////////////
var isuniqueArray=new Array("LICALTID","LICID");//一維數(shù)組,表的字段名
var isunique_str="";
var isunique_column="";
for(var i=0;i<isuniqueArray.length;i++){
//1:唯一性枚舉值
? isunique_str+="var "+isuniqueArray[i]+"_isunique=0;";
? //唯一性校驗枚舉值賦值
? isunique_str+="var uniquesql_"+isuniqueArray[i]+"=\"SELECT count(1) FROM "+check_table+" where "+isuniqueArray[i]+"='\"+"+isuniqueArray[i]+"+\"' \";";
? isunique_str+="if(fireToDB(strConn,uniquesql_"+isuniqueArray[i]+")[0][0]==1){"+isuniqueArray[i]+"_isunique=1;}";
? //校驗所有表需要校驗的字段,如果有一個校驗失敗,則校驗狀態(tài)為1
? isunique_str+="if(check_status==0){if("+isuniqueArray[i]+"_isunique==0)check_status=1;}";
? //check is unique? return not unique column
? isunique_str+="if("+isuniqueArray[i]+"_isunique==0){if(isunique_column==\"\"){ isunique_column = \""+ isuniqueArray[i] +"\";}else{isunique_column+=\"、\"+\""+isuniqueArray[i]+"\";}}";?
}
//最終輸出的錯誤詳細情況
isunique_str+="if(check_status==1){check_type=\"違反唯一規(guī)則\";check_detail=\"表\"+source_table+\"中,字段\"+isunique_column+\"違反了唯一規(guī)則\";}";
eval(isunique_str);
//////////////////////////////////////////////////////////////////////////////////
/////////////////////////////字段非空校驗//////////////////////////////////////////
var isnullArray=new Array("ALT","ALTBE","ALTAF","ALTDATE");//一維數(shù)組,表的字段名
var isnull_str="";
var isnull_column="";
check_status=0;//校驗狀態(tài)置0
for(var i=0;i<isnullArray.length;i++){
//2:非空枚舉值
isnull_str+="var "+isnullArray[i]+"_isnull=0;";
//非空校驗枚舉值賦值
isnull_str+="if("+isnullArray[i]+"==null||"+isnullArray[i]+"==\"\"){"+isnullArray[i]+"_isnull=1;}";
//校驗所有表需要校驗的字段,如果有一個校驗失敗,則校驗狀態(tài)為1
isnull_str+="if(check_status==0){if("+isnullArray[i]+"_isnull==1)check_status=1;}";
//check is null? return null column
isnull_str += "if("+isnullArray[i]+"_isnull==1){if(isnull_column==\"\"){isnull_column = \""+ isnullArray[i] +"\";}else{isnull_column+=\"、\"+\""+isnullArray[i]+"\";}}";
}
//最終輸出的錯誤詳細情況
isnull_str+="if(check_status==1){if(check_detail==\"\"){check_type=\"違反非空規(guī)則\";check_detail=\"字段\"+ isnull_column +\"違反了非空規(guī)則\";}else{check_type+=\",\"+\"違反非空規(guī)則\";check_detail+=\",字段\"+ isnull_column +\"違反了非空規(guī)則\";}}";
eval(isnull_str);
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////標準值校驗//////////////////////////////////////////
var isnormalArray = [["S_EXT_FROMNODE","in ('220000')"],["ALT","in ('許可文件編號','許可文件名稱','許可機關(guān)','許可內(nèi)容')"]];//二維數(shù)組,第一列為字段,第二列為匹配規(guī)則
var isnormal_str="";
var isnormal_column="";
check_status=0;//校驗狀態(tài)置0
for(var i=0;i<isnormalArray.length;i++){
//3:標準化枚舉值
isnormal_str+="var "+isnormalArray[i][0]+"_isnormal=0;";
//標準化校驗枚舉值賦值
isnormal_str+="var normalsql_"+isnormalArray[i][0]+"= \"select? count(1) from "+check_table+" where "+ isnormalArray[i][0] +" "+isnormalArray[i][1]+" and "+check_table_id+"='"+check_name+"'\";";
isnormal_str+="if(fireToDB(strConn, normalsql_"+isnormalArray[i][0]+")[0][0]>0){"+ isnormalArray[i][0] +"_isnormal=1;}";
//校驗所有表需要校驗的字段,如果有一個校驗失敗,則校驗狀態(tài)為1
isnormal_str+="if(check_status==0){if("+isnormalArray[i][0]+"_isnormal==0) check_status=1;}";
//check is normal? return not normal column
isnormal_str+="if("+isnormalArray[i][0]+"_isnormal==0){if(isnormal_column==\"\"){ isnormal_column = \""+ isnormalArray[i][0] +"\";}else{isnormal_column+=\"、\"+\""+isnormalArray[i][0]+"\";}}";
}
//最終輸出的錯誤詳細情況
isnormal_str+="if(check_status==1){if(check_detail==\"\"){check_type=\"違反標準化規(guī)則\";check_detail=\"字段\"+ isnormal_column +\"違反了標準化規(guī)則\";}else{check_type+=\",\"+\"違反標準化規(guī)則\";check_detail+=\",字段\"+ isnormal_column +\"違反了標準化規(guī)則\";}}";
eval(isnormal_str);
//////////////////////////////數(shù)據(jù)類型校驗//////////////////////////////////////////
var datatypeArray = [["ALTDATE","isDate"],["ALTAF","isNum"]];//二維數(shù)組,第一列為要校驗的數(shù)據(jù)字段,第二列為數(shù)據(jù)類型校驗函數(shù)(isDate[日期]、isNum[數(shù)字]、isMailValid[郵箱]、isEmpty[空])
var datatype_str="";
var datatype_column="";
check_status=0;//校驗狀態(tài)置0
for(var i=0;i<datatypeArray.length;i++){
//4:類型校驗枚舉值
datatype_str+="var "+datatypeArray[i][0]+"_datatype=0;";
//數(shù)據(jù)類型校驗枚舉值賦值
datatype_str+="var datatypesql_"+datatypeArray[i][0]+"=\"select "+datatypeArray[i][0]+" from "+ check_table +" where "+check_table_id+"='"+check_name+"'\";";
datatype_str+="if("+datatypeArray[i][1]+"(fireToDB(strConn,datatypesql_"+datatypeArray[i][0]+")[0][0])) "+ datatypeArray[i][0]+"_datatype=1;";
//校驗所有需要校驗的字段,如果有一個校驗失敗,則校驗狀態(tài)為1
datatype_str+="if(check_status==0){if("+datatypeArray[i][0]+"_datatype==0) check_status=1;}";
//check is datatype? return not datatype column
datatype_str+="if("+datatypeArray[i][0]+"_datatype==0){if(datatype_column==\"\"){ datatype_column =\""+datatypeArray[i][0]+"\";}else{datatype_column+=\"、"+datatypeArray[i][0]+"\";}}";
}
//最終輸出的錯誤詳細情況
datatype_str+="if(check_status==1){if(check_detail==\"\"){check_type=\"違反數(shù)據(jù)類型規(guī)則\";check_detail=\"字段\"+datatype_column+\"違反數(shù)據(jù)類型規(guī)則\";}else{check_type+=\",\"+\"違反數(shù)據(jù)類型規(guī)則\";check_detail+=\",字段\"+datatype_column+\"違反數(shù)據(jù)類型規(guī)則\";}}";
eval(datatype_str);
//////////////////////////////////////////////////////////////////////////////////////
if(check_detail!="")
{
check_detail=check_detail+","+source_table_id+"="+check_name;
}
以下是執(zhí)行后的效果:
在此基礎(chǔ)上我們還可以擴展更多的校驗,比如通過正則表達式的方式(利用kettle的isRegExp函數(shù)),如下:
//////////////////////////////正則表達式校驗//////////////////////////////////////////
var dataArray = [["Email","^\\w+@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}$","^[\\w-\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"]];//二維數(shù)組,第一列為要校驗的數(shù)據(jù)字段,第二列為規(guī)則1,第三列為規(guī)則2(可選)
var data_str="";
var data_column="";
check_status=0;//校驗狀態(tài)置0
for(var i=0;i
//4:類型校驗枚舉值
data_str+="var "+dataArray[i][0]+"_data=0;";
//數(shù)據(jù)類型校驗枚舉值賦值
data_str+="var datasql_"+dataArray[i][0]+"=\"select "+dataArray[i][0]+" from "+check_table+" where "+ check_table_id +"='"+check_name+"'\";";
data_str+="if(isRegExp(fireToDB(strConn,datasql_"+dataArray[i][0]+")[0][0],dataArray[i][1],dataArray[i][2] )>-1) "+dataArray[i][0]+"_data=1;";
//校驗所有需要校驗的字段,如果有一個校驗失敗,則校驗狀態(tài)為1
data_str+="if(check_status==0){if("+dataArray[i][0]+"_data==0) check_status=1;}";
//check is data? return not data column
data_str+="if("+dataArray[i][0]+"_data==0){if(data_column==\"\"){data_column=\""+ dataArray[i][0] + "\"; } else {data_column+=\"、"+dataArray[i][0]+"\";}}";
}
//最終輸出的錯誤詳細情況
data_str+="if(check_status==1){if(check_detail==\"\"){check_type=\"違反數(shù)據(jù)類型規(guī)則\";check_detail=\"字段\"+ data_column +\"違反數(shù)據(jù)類型規(guī)則\";}else{check_type+=\",\"+\"違反數(shù)據(jù)類型規(guī)則\";check_detail+=\",字段\"+ data_column + \"違反數(shù)據(jù)類型規(guī)則\";}}";eval(data_str);
以上腳本的好處就是,可以直接通過修改變量,就能對不同輸出表的不同字段進行校驗,基本上不用修改邏輯代碼,如果進一步優(yōu)化一下,就可以寫成函數(shù),直接調(diào)用。可以把檢驗字段和檢驗規(guī)則進一步參數(shù)化,通過調(diào)用參數(shù)表,來執(zhí)行數(shù)據(jù)驅(qū)動的校驗測試(這就是一種自動化測試的思路)。