血缘解析是数据治理中很关键的一环,本文着重讲解血缘解析的思路,如何把一段sql进行字段级别的解析,最终插入到数据库的数据表中,如下所示
target_tab | target_col | source_tab | source_col | source_flag | is_valid | calc_meth |
---|---|---|---|---|---|---|
一段sql是如何被解析执行的呢?
无论是关系型数据库还是spark、hive等大数据引擎,第一步都是生成AST抽象语法树,流程如下
一般的AST抽象语法树如下所示
SELECT
|
[Columns]
/ | \
[Column] [Column] [Column]
| | |
id name age
|
FROM
|
[Table]
|
users
try{
statements = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL);
}catch (Exception e){
System.out.println("can't parser by druid mysql"+e);
}
上述代码是用druid sql来解析,常用的解析工具还有antlr4
name | desc |
---|---|
targetColumnName | 目标字段 |
sourceDbName | 字段来源Db |
sourceTableName | 字段来源表 |
sourceColumnName | 字段来源列 |
expression | 表达式,字段的加工过程 |
isEnd | 是否结束标识 |
比如说一段Sql传进来后,会先解析为AST抽象语法树
1、先传入Sql到解析入口,判断Sql是单独select语句还是包含union的select语句,若是包含union,通过 SQLUnionQuery.getLeft()和getRight()方法拆分union语句,再把拆分的语句放到解析的入口
columnLineageAnalyzer(((SQLUnionQuery) sqlSelectQuery).getLeft().toString(),node);
columnLineageAnalyzer(((SQLUnionQuery) sqlSelectQuery).getRight().toString(),node);
2、把单独的select语句或者union拆分的select语句传进来后,首先获取字段列表
List<SQLSelectItem> selectItems = sqlSelectQueryBlock.getSelectList();
然后遍历字段列表,通过getAlias获取字段的别名,若没有别名即为本身,别名就是targetColumnName,可以调用血缘关系对象的setTargetColumnName()方法
3、通过getExpr()获取字段的名称,也就是as之前的内容,若有加工过程也可获取到,然后调用setExpression()
4、解析表达式,上一步getExpr可能得到的字段是有多个字段加工而来,需要进行处理,解析表达式有如下种类,针对不同的类型有不同的处理方法,最终调用setSourceColumnName
private static void handlerExpr(SQLExpr sqlExpr,TreeNode<LineageColumn> itemNode) {
//方法
if (sqlExpr instanceof SQLMethodInvokeExpr){
visitSQLMethodInvoke( (SQLMethodInvokeExpr) sqlExpr,itemNode);
}
//聚合
else if (sqlExpr instanceof SQLAggregateExpr){
visitSQLAggregateExpr((SQLAggregateExpr) sqlExpr,itemNode);
}
//case
else if (sqlExpr instanceof SQLCaseExpr){
visitSQLCaseExpr((SQLCaseExpr) sqlExpr,itemNode);
}
//比较
else if (sqlExpr instanceof SQLBinaryOpExpr){
visitSQLBinaryOpExpr((SQLBinaryOpExpr) sqlExpr,itemNode);
}
//表达式
else if (sqlExpr instanceof SQLPropertyExpr){
visitSQLPropertyExpr((SQLPropertyExpr) sqlExpr,itemNode);
}
//列
else if (sqlExpr instanceof SQLIdentifierExpr){
visitSQLIdentifierExpr((SQLIdentifierExpr) sqlExpr,itemNode);
}
//赋值表达式
else if (sqlExpr instanceof SQLIntegerExpr){
visitSQLIntegerExpr((SQLIntegerExpr) sqlExpr,itemNode);
}
//数字
else if (sqlExpr instanceof SQLNumberExpr){
visitSQLNumberExpr((SQLNumberExpr) sqlExpr,itemNode);
}
//字符
else if (sqlExpr instanceof SQLCharExpr){
visitSQLCharExpr((SQLCharExpr) sqlExpr,itemNode);
//标量子查询
} else if (sqlExpr instanceof SQLQueryExpr) {
columnLineageAnalyzer(sqlExpr.toString(), itemNode);
}
}
5、字段处理完后,处理table,table有如下几种类型,每一种的处理方式也不一样
if (isContinue.get()){
// 获取表
SQLTableSource table = sqlSelectQueryBlock.getFrom();
// 普通单表
if (table instanceof SQLExprTableSource) {
// 处理最终表---------------------
handlerSQLExprTableSource(node, (SQLExprTableSource) table);
} else if (table instanceof SQLJoinTableSource) {
//处理join
handlerSQLJoinTableSource(node, (SQLJoinTableSource) table);
} else if (table instanceof SQLSubqueryTableSource) {
// 处理 subquery ---------------------
handlerSQLSubqueryTableSource(node, table);
}else if (table instanceof SQLUnionQueryTableSource) {
// 处理 union ---------------------
handlerSQLUnionQueryTableSource(node, (SQLUnionQueryTableSource) table);
}
}