我們?cè)诘?篇的時(shí)候已經(jīng)介紹了將Excel的數(shù)據(jù)導(dǎo)入到Mysql數(shù)據(jù)庫(kù)中,那么,本章我們將介紹將數(shù)據(jù)從數(shù)據(jù)庫(kù)導(dǎo)出到Excel中.
我們數(shù)據(jù)庫(kù)表數(shù)據(jù)如下圖:
數(shù)據(jù)庫(kù)中總共存在36條數(shù)據(jù),這是數(shù)據(jù)比較少的情況
我們選擇 文件 -> 新建 ->轉(zhuǎn)換
建立導(dǎo)出Excel的轉(zhuǎn)換,輸入轉(zhuǎn)換名稱(chēng)然后保存
既然是從數(shù)據(jù)庫(kù)表導(dǎo)出數(shù)據(jù),所以我們的ETL的第一個(gè)步驟就是表輸入
新建數(shù)據(jù)庫(kù)連接
在選擇表輸入組件時(shí),我們首先需要?jiǎng)?chuàng)建我們的數(shù)據(jù)庫(kù)連接
點(diǎn)擊新建按鈕,在彈出的數(shù)據(jù)庫(kù)編輯框中填入數(shù)據(jù)庫(kù)信息,如下圖:
在輸入完成后,我們可以點(diǎn)擊測(cè)試按鈕對(duì)數(shù)據(jù)庫(kù)連接進(jìn)行測(cè)試,以查看數(shù)據(jù)庫(kù)是否可用
信息無(wú)誤后,點(diǎn)擊確定
此時(shí),我們可以輸入我們的查詢(xún)SQL語(yǔ)句對(duì)表進(jìn)行查詢(xún)以獲取結(jié)果
點(diǎn)擊預(yù)覽按鈕,彈出預(yù)覽記錄數(shù)量限制數(shù)量設(shè)置,即可以預(yù)覽數(shù)據(jù):
預(yù)覽無(wú)誤,說(shuō)明我們的信息是正確的,我們的表輸入最終屬性配置信息如下圖:
因?yàn)槲覀兛倲?shù)只有36條記錄,因此在記錄數(shù)量限制這里不妨可以設(shè)置一個(gè)最大值,此處我設(shè)置的是40
因?yàn)槲覀冏罱K是通過(guò)Excel輸出,因此我們從轉(zhuǎn)換的核心對(duì)象樹(shù)的輸出欄 選擇Microsoft Excel輸出組件
設(shè)置輸出組件的屬性
因?yàn)楸容^簡(jiǎn)單,因此我們只需要設(shè)置導(dǎo)出的Excel文件名稱(chēng)即可,如下圖:
此時(shí)我們的ETL轉(zhuǎn)換已創(chuàng)建完成,如下圖:
此時(shí),我們點(diǎn)擊Spoon界面上的運(yùn)行按鈕,執(zhí)行該轉(zhuǎn)換過(guò)程,導(dǎo)出Excel結(jié)果如下:
針對(duì)較少的數(shù)據(jù),利用Kettle非常輕松的幫助我們導(dǎo)出了數(shù)據(jù)到Excel中.
我們?cè)谏蟼€(gè)步驟中將數(shù)據(jù)庫(kù)表中較少數(shù)據(jù)(36條)導(dǎo)出到了Excel中,這幾乎沒(méi)什么難度,那么如果我們的數(shù)據(jù)庫(kù)表中數(shù)據(jù)比較多時(shí),是否也能按這種方式導(dǎo)出呢?
答案肯定是否定的,因?yàn)槿绻覀円淮尾樵?xún)數(shù)據(jù)較多的話(huà),很可能導(dǎo)致內(nèi)存溢出的異?;蛘逰ettle直接就崩潰了.
此時(shí)我們可以使用分頁(yè)技術(shù),來(lái)將我們的數(shù)據(jù)按頁(yè)碼批量導(dǎo)出
我們數(shù)據(jù)庫(kù)擁有fund表,此時(shí),我們想通過(guò)Kettle將fund表的記錄全部導(dǎo)出,我們應(yīng)該怎么做呢?
我們首先先按照分頁(yè)來(lái)進(jìn)行數(shù)據(jù)的導(dǎo)出
新建轉(zhuǎn)換
文件 -> 新建 -> 轉(zhuǎn)換
保存轉(zhuǎn)換名為:分頁(yè)導(dǎo)出數(shù)據(jù)
設(shè)置分頁(yè)變量
我們都知道Mysql中可以使用limit關(guān)鍵字來(lái)進(jìn)行分頁(yè)查詢(xún)數(shù)據(jù),因此第一步,我們需要通過(guò)生成記錄組件定義兩個(gè)變量,分別是:
表輸入
設(shè)置好變量后,我們可以拖入表輸入組件,進(jìn)行相關(guān)的屬性設(shè)置
分別設(shè)置SQL查詢(xún)語(yǔ)句,注意我們?cè)赟QL語(yǔ)句中使用了Mysql的limit分頁(yè),并且通過(guò)前面的變量來(lái)代替相關(guān)的值
如下圖:
Microsoft Excel輸出
最后,我們通過(guò)Excel輸出組件配置導(dǎo)出到Excel
這里我們需要注意的是,擴(kuò)展類(lèi)型我們需要選擇xlsx格式(因?yàn)?7格式會(huì)有總記錄的條數(shù)限制)
運(yùn)行
此時(shí),我們的最終轉(zhuǎn)換如下圖:
最終查看我們導(dǎo)出的數(shù)據(jù)如下:
上面我們使用了分頁(yè)的方式將我們的數(shù)據(jù)按頁(yè)碼導(dǎo)到了Excel中,如果我們想把數(shù)據(jù)表中的全部數(shù)據(jù)都導(dǎo)入到Excel,應(yīng)該如何做呢?
此時(shí),我們可以把分頁(yè)導(dǎo)出轉(zhuǎn)換作為一個(gè)作業(yè)子項(xiàng),我們?cè)谧鳂I(yè)子項(xiàng)中設(shè)置分頁(yè)條件,輪訓(xùn)總頁(yè)碼進(jìn)行批量導(dǎo)出,核心點(diǎn)在于我們只需要設(shè)置offsetSize變量,然后輪訓(xùn)進(jìn)行替換即可
我們需要兩個(gè)計(jì)算頁(yè)碼的公式
根據(jù)記錄總數(shù)計(jì)算總頁(yè)碼:
var totalPage= (totalRows+pageSize-1) / pageSize;
計(jì)算Mysql中的offset值
var nowOffSize= pageSize * page ;
作業(yè)圖
我們先來(lái)梳理一下我們這個(gè)全部導(dǎo)出作業(yè)需要做的事情:
因此,我們起碼需要一個(gè)作業(yè)和兩個(gè)轉(zhuǎn)換,才能幫助我們完成數(shù)據(jù)的全部導(dǎo)出任務(wù)
先來(lái)看我們已經(jīng)完成的作業(yè)圖,如下圖:
接下來(lái)我們逐步分析我們每個(gè)步驟的邏輯
新建作業(yè)
新建全部導(dǎo)出作業(yè)
選擇 文件 -> 新建 作業(yè)
設(shè)置變量
我們?cè)诮⒆鳂I(yè)任務(wù)時(shí),因?yàn)樾枰褂梅猪?yè)技術(shù)對(duì)數(shù)據(jù)進(jìn)行查詢(xún),因此我們的分頁(yè)導(dǎo)出數(shù)據(jù)中的轉(zhuǎn)換SQL語(yǔ)句就不能使用常量,必須使用變量。因此我們的作業(yè)第一步是設(shè)置變量,如下圖:
我們主要定義了5個(gè)變量并賦予初始值,并且變量的方位都是在JVM中有效
獲取目標(biāo)數(shù)據(jù)表總記錄數(shù)
第二步我們就需要新建一個(gè)轉(zhuǎn)換,用來(lái)獲取我們的目標(biāo)表總記錄條數(shù),轉(zhuǎn)換如下圖:
這個(gè)轉(zhuǎn)換很簡(jiǎn)單,只有兩個(gè)組件:表輸入、復(fù)制記錄到結(jié)果
表輸入組件是我們編寫(xiě)從數(shù)據(jù)庫(kù)查詢(xún)目標(biāo)表的統(tǒng)計(jì)SQL語(yǔ)句,如下圖:
統(tǒng)計(jì)fund
表的總記錄SQL語(yǔ)句:
SELECT count(*) fundCount from fund
然后將我們的結(jié)果復(fù)制到結(jié)果即可
JavaScript腳本-計(jì)算總頁(yè)數(shù)
我們得到了目標(biāo)表的總記錄數(shù),接下來(lái)我們需要一段JavaScript腳本幫助我們計(jì)算得到總頁(yè)數(shù),腳本內(nèi)容如下:
var preRows=previous_result.getRows();//獲取上一個(gè)步驟的結(jié)果集 var subject="自定義日志輸出"; var logFactory = new org.pentaho.di.core.logging.LogChannelFactory(); var log= logFactory.create(subject); if(preRows==null || preRows.size()==0){ false; }else{ var countBySql=preRows.get(0).getInteger("fundCount"); //賦值變量 var pageSize=parent_job.getVariable("pageSize"); log.logMinimal("pageSize:"+pageSize+",countRecords:"+countBySql); //計(jì)算總頁(yè)碼 var totalPage=com.xiaominfo.kettle.util.PaginationUtils.totalPage(countBySql,pageSize); log.logMinimal("totalPage:"+totalPage); //設(shè)置總頁(yè)碼 parent_job.setVariable("totalPage",totalPage); true; }
這里有幾個(gè)組件需要說(shuō)明一下:
previous_result
用以獲取上一個(gè)步驟的記錄行數(shù),因?yàn)槲覀兩弦粋€(gè)步驟是獲取總記錄數(shù)的轉(zhuǎn)換(記錄行已經(jīng)復(fù)制到結(jié)果),關(guān)于JavaScript腳本的內(nèi)置對(duì)象,表達(dá)式介紹可以參考Kettle實(shí)戰(zhàn)100篇 第11篇 JavaScript表達(dá)式變量說(shuō)明PaginationUtils
,該類(lèi)是我開(kāi)發(fā)的工具集,方便在JavaScript腳本中使用快速計(jì)算的,totalPage方法就是一個(gè)根據(jù)總記錄數(shù)以及pageSize來(lái)得到總頁(yè)數(shù)的函數(shù),關(guān)于自定義開(kāi)發(fā)的功能,可以參考Kettle實(shí)戰(zhàn)100篇 第12篇 自定義開(kāi)發(fā)Java工具類(lèi)并在JavaScript腳本中運(yùn)用parent_job
內(nèi)置對(duì)象的setVariable
方法再將我們得到的結(jié)果在重新賦值.檢查字段的值
初始化好我們的總頁(yè)碼后,接下來(lái)我們就需要設(shè)置輪訓(xùn)分頁(yè)條件了,如下圖
設(shè)置當(dāng)前的頁(yè)面小于等于總頁(yè)碼數(shù),符合條件即進(jìn)行分頁(yè)Excel導(dǎo)出轉(zhuǎn)換的操作,否則程序結(jié)束.
分頁(yè)導(dǎo)出Excel轉(zhuǎn)換
在分頁(yè)導(dǎo)出Excel轉(zhuǎn)換中,區(qū)別于較少數(shù)據(jù)的轉(zhuǎn)換,我們需要從父作業(yè)中獲取變量,然后傳遞到子轉(zhuǎn)換中的相關(guān)組件中使用變量,所以整個(gè)子轉(zhuǎn)換如下圖:
第一步是獲取變量,該操作和我們上面較少數(shù)據(jù)導(dǎo)出其實(shí)是大同小異,無(wú)非是把生成記錄組件中定義的變量替換使用父作業(yè)中的變量 ,如下圖:
定義子轉(zhuǎn)換中的相關(guān)變量。
第二步是表輸入組件,分頁(yè)查詢(xún)數(shù)據(jù),如下圖:
在表輸入組件中,我們使用定義的變量代替SQL語(yǔ)句中的limit分頁(yè)數(shù)值,然后勾選替換SQL語(yǔ)句里的變量選項(xiàng)已經(jīng)使用懶惰算法選項(xiàng),記錄數(shù)量限制為0(即不限制)
最后我們選擇Microsoft Excel 輸出組件,把我們的結(jié)果輸出到Excel中
因?yàn)槲覀儾⒎鞘且淮稳繉?dǎo)出,而是采取的分頁(yè),因此在設(shè)置好文件名及文件擴(kuò)展后,需要選擇如果文件已存在則使用現(xiàn)有文件輸出
工作表選項(xiàng)卡中如果輸出文件中已存在工作表也選擇繼續(xù)輸出至已存在的工作表中
然后是內(nèi)容選項(xiàng)卡:
此處需要設(shè)置楷書(shū)輸出子單元格的變量,即我們父作業(yè)中定義的shellFirst
變量,在Excel的Sheet表格中即代表從哪一行開(kāi)始輸出數(shù)據(jù)
然后勾選在表的末尾開(kāi)始寫(xiě)(追加行)選項(xiàng),最后點(diǎn)擊確定保存
檢查頁(yè)碼條件
接下來(lái)我們需要檢查賦值我們的頁(yè)碼,通過(guò)JavaScript腳本來(lái)實(shí)現(xiàn),腳本內(nèi)容如下:
var page=parent_job.getVariable('currentPage'); var totalPage=parent_job.getVariable('totalPage'); var subject="自定義日志"; var log= new org.pentaho.di.core.logging.LogChannel(subject); if(page==totalPage){ false; }else{ //設(shè)置offsetSize的值 var pageSize=parent_job.getVariable('pageSize'); //在page++之前先計(jì)算offset的值 //offset方法為page*pageSize var nowOffSize=com.xiaominfo.kettle.util.PaginationUtils.offset(page,pageSize); page++; parent_job.setVariable('currentPage',page); var shellFirst=parent_job.getVariable('shellFirst'); //日志輸出 log.logMinimal("offset:"+nowOffSize); parent_job.setVariable('offsetSize',nowOffSize); var shellNum=nowOffSize+1; var newShellFirst=shellFirst.substring(0,1)+shellNum; log.logMinimal("Shell單元格開(kāi)始輸出記錄行:"+newShellFirst); parent_job.setVariable('shellFirst',newShellFirst); true; }
該代碼邏輯主要步驟:
parent_job
內(nèi)置對(duì)象重新賦值當(dāng)前頁(yè)碼變量shellFirst
變量執(zhí)行
整個(gè)作業(yè)過(guò)程完成,運(yùn)行該作業(yè),得到我們導(dǎo)出的該fund表的全部數(shù)據(jù)24332
條
表輸入組件預(yù)覽數(shù)據(jù)、導(dǎo)出Excel數(shù)據(jù)亂碼
該問(wèn)題我在使用分頁(yè)查詢(xún)導(dǎo)出的時(shí)候碰到了亂碼的情況,我的情況比較特殊,我通過(guò)瀏覽已經(jīng)建立好的數(shù)據(jù)庫(kù)連接的中的數(shù)據(jù)時(shí)并非亂碼,而當(dāng)我使用表輸入組件中的預(yù)覽數(shù)據(jù)時(shí)缺產(chǎn)生了亂碼,因此我不得不設(shè)置我們的數(shù)據(jù)庫(kù)連接參數(shù)
亂碼主要分幾種情況
一、查看我們的數(shù)據(jù)庫(kù)的服務(wù)端字符集是否是UTF-8(常用字符集)
可以使用navicat連接到我們的數(shù)據(jù)庫(kù),然后使用命令行,輸入查詢(xún)語(yǔ)句進(jìn)行查看,如下:
- mysql> show variables like '%char%';
- +--------------------------+-----------------------------------------------+
- | Variable_name | Value |
- +--------------------------+-----------------------------------------------+
- | character_set_client | utf8mb4 |
- | character_set_connection | utf8mb4 |
- | character_set_database | utf8 |
- | character_set_filesystem | binary |
- | character_set_results | utf8mb4 |
- | character_set_server | utf8 |
- | character_set_system | utf8 |
- | character_sets_dir | D:\Users\xiaoymin\Bin\mariadb\share\charsets\ |
- +--------------------------+-----------------------------------------------+
- 8 rows in set (0.08 sec)
其中character_set_server
就是我們的數(shù)據(jù)庫(kù)服務(wù)端編碼
我們也可以使用SQL語(yǔ)句查詢(xún)我們的表字段編碼,如下:
mysql> show full columns from fund;
如果我們第一步檢查是OK的,但是瀏覽數(shù)據(jù)依然是亂碼,那么我們就需要修改Kettle中的配置參數(shù)
點(diǎn)擊表輸入組件的編輯按鈕,對(duì)數(shù)據(jù)庫(kù)信息進(jìn)行編輯
1、選擇高級(jí)選項(xiàng)卡,添加相關(guān)字符碼
2、高級(jí)選項(xiàng)卡中設(shè)置names值,網(wǎng)上的解決方案大多是使用utf8,但是我本機(jī)使用后發(fā)現(xiàn)還是亂碼,因此我改成了gbk,這個(gè)大家自行根據(jù)自己的情況設(shè)定
set names gbk;
如下圖:
聯(lián)系客服