小程序:DW元數(shù)據(jù)表血緣關(guān)系的實現(xiàn) - lixiaotaoplus的專欄 - 博客頻道 - CSDN.NET http://blog.csdn.net/lixiaotaoplus/article/details/52840237
隨著數(shù)據(jù)倉庫(DW)接入的表和建立的模型增多,元數(shù)據(jù)管理就變得越來越重要。元數(shù)據(jù)表血緣關(guān)系,俗稱“表與表之間的關(guān)系”。良好的元數(shù)據(jù)管理,可以清晰和明確看出每張表和模型之前的關(guān)系。
在沒有工具之前,只能依靠手工維護,一旦腳本發(fā)生變化,手工維護遺漏或不及時的話,就會造成關(guān)系不準確。通過工具,當表數(shù)量上百、上千張的時候,通過分析表與表“血緣關(guān)系”,就能清楚知道每張表之間的關(guān)系,及時定位和溯源問題。
筆者在XXX項目實踐中,通過Java和Hive,最終產(chǎn)出一張表與表之間的關(guān)系表。現(xiàn)在把思路和代碼分享給大家,與大家一起交流。當然,程序也有會很多改進和完善的地方,如有不妥,歡迎指正,謝謝。
本文也提供了解析sql的思路和方法。
實現(xiàn)思路:
獲取輸入路徑;
讀取文本文件內(nèi)容(其中需要把從路徑中讀取所有文件);
規(guī)則解析
“來源表”解析:主要通過表命名規(guī)范進行解析
“目標表”解析:主要通過insert into table和insert overwrite table語句解析
將解析文件,輸出txt文件;
將文件上傳到hdfs文件,加載到HIVE;
HIVE“行轉(zhuǎn)列”處理;
生成“文件目錄filepath、來源表source_table、目標表target_table ”三個字段.
主要代碼:
<一>、獲取輸入路徑主要代碼:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
ArrayList<File> files=getListFiles(D:\\HQL\xx); //獲取解析文件路徑
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
public static ArrayList<File> getListFiles(Object obj) {
File directory = null;
if (obj instanceof File) {
directory = (File) obj;
} else {
directory = new File(obj.toString());
}
ArrayList<File> files = new ArrayList<File>();
if (directory.isFile()) {
files.add(directory);
return files;
} else if (directory.isDirectory()) {
File[] fileArr = directory.listFiles();
for (int i = 0; i < fileArr.length; i++) {
File fileOne = fileArr[i];
files.addAll(getListFiles(fileOne));
}
}
return files;
<二>、讀取文本文件內(nèi)容主要代碼
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
String filePath =files.get(i).toString(); //獲取的單個文件路徑
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
//讀取文本文件內(nèi)容
public static String readFile(String filePath) throws IOException {
StringBuffer sb = new StringBuffer();
readToBuffer(sb, filePath);
return sb.toString();
}
//將文本文件中的內(nèi)容讀入到buffer中
lic static void readToBuffer(StringBuffer buffer, String filePath) throws IOException {
InputStream is = new FileInputStream(filePath);
String line; // 用來保存每行讀取的內(nèi)容
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
line = reader.readLine(); // 讀取第一行
while (line != null) { // 如果 line 為空說明讀完了
buffer.append(line); // 將讀到的內(nèi)容添加到 buffer 中
buffer.append("\n"); // 添加換行符
line = reader.readLine(); // 讀取下一行
}
reader.close();
is.close();
}
<三>、解析“來源表”主要代碼
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
public static String hqltoSourceTable(String hql){
//獲取hql內(nèi)容
Map<String,String> map = new HashMap<String,String>();
String pattern = "(<span style="line-height: 23.8px; font-family: 微軟雅黑, 宋體, arial;">ods.|dwd.|......</span><span style="line-height: 23.8px; font-family: 微軟雅黑, 宋體, arial;">)\w+" ;//此處可以添加表命名規(guī)則</span>
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(hql.toLowerCase().replaceAll(" \s+"," "));//轉(zhuǎn)小寫,將“空白字符”轉(zhuǎn)空格
while(m.find()) {
map.put(m.group(0), m.group(0));
}
return map.keySet().toString().replaceAll("[\[\] +]", "");
}
<四>、解析“目標表”主要代碼
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
public static String hqltoTargetTable(String hql){
//獲取hql內(nèi)容
Map<String,String> map = new HashMap<String,String>();
//表:
String pattern = "(insert into table|insert overwrite table)\s+((\w+)\.(\w+)|(\w+))" ;
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(hql.toLowerCase().replaceAll(" \s+"," "));//轉(zhuǎn)小寫,將“空白字符”轉(zhuǎn)空格
while(m.find()) {
map.put(m.group(0).replaceAll("insert into table|insert overwrite table|ods.|dwd.|.....", ""), m.group(0));//此處需要添加庫名規(guī)則,將庫名過濾掉
}
return map.keySet().toString().replaceAll("[\[\] \s+]", "");
五、將解析文件,輸出txt文件
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
StringBuffer bf =new StringBuffer();
ileUtil fileUtil = new FileUtil();
fileUtil.writerFile(path,"hqlToTable.txt",bf.toString());
System.out.println("succeed! The file path is "+ path + "/hqlToTable.txt");
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
package com.xx.xx.hql;
import java.io.*;
public class FileUtil {
public String readerFile(String path) throws IOException {
File file = new File(path);
if (!file.exists() || file.isDirectory())
throw new FileNotFoundException();
byte[] tempbytes = new byte[5120];
int length=0;
StringBuffer sb = new StringBuffer();
@SuppressWarnings("resource")
FileInputStream fin = new FileInputStream(path);
// 讀入多個字節(jié)到字節(jié)數(shù)組中,byteread為一次讀入的字節(jié)數(shù)
while ((length=fin.read(tempbytes)) != -1) {
sb.append(new String(tempbytes,0,length));
}
return sb.toString();
}
public boolean writerFile(String path, String fileName, String fileContent) throws IOException {
try {
File file = new File(path);
if (!file.isDirectory()) {
file.deleteOnExit();
file.mkdirs();
}
String filePath = file + "/" + fileName;
file = new File(filePath);
if (file.exists() || file.isFile()) {
file.delete();
}
BufferedWriter out
= new BufferedWriter(new FileWriter(filePath));
out.write(fileContent);
out.flush();
out.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
六、HIVE“行轉(zhuǎn)列”處理主要代碼
[sql] view plain copy 在CODE上查看代碼片派生到我的代碼片
load data inpath '/tmp/hqlToTable.txt' into table tmp_app_xxxxx_table_rel_a;
insert overwrite table app_xxxxx_table_rel_a partition(dt='${hivevar:today}')
select
source_table
,target_table
,filepath
,from_unixtime(unix_timestamp())
from
(
SELECT distinct
filepath
,trim(new_source_table) as source_table
,trim(new_target_table) as target_table
FROM (
SELECT filepath
,source_table
,target_table
FROM tmp_app_xxxxx_table_rel_a --將解析后的文件數(shù)據(jù)加載到此表里
WHERE target_table IS NOT NULL
AND target_table <> ''
AND (
filepath NOT LIKE '%create%'
OR filepath NOT LIKE '%bak%'
OR filepath NOT LIKE '%test%'
OR filepath NOT LIKE '%bkt%'
)
) t LATERAL VIEW explode(split(source_table, ',')) adTable AS new_source_table
LATERAL VIEW explode(split(target_table, ',')) adTable AS new_target_table
) t
where trim(source_table) <> trim(target_table)
;