hive的表关联效率较低,应用中大表对外键小表的outer关联效率无法忍受,如果用python做个脚本在hive中处理很好。
forhive.py关联的两个函数
memjoin读入数据文件到内存,与hive中的数据表进行join,需要7个参数,如果关联多个文件则7+6x个参数第一个参数 tabledefinefile 文件中需给出数据表的定义,数据表名称 : 字段名称1,>字段名称2,...,字段名称n,参考tabledef.txt,数据表名称可以是后面文件名,如果不完全
一样文件路径需要包含数据表名称字样,被处理的数据表名称用的是stdin(一般为pvlog数>据表)
第二个参数 joinfields hive数据表要关联的字段列表,用逗号,连接
第三个参数 filepath 关联数据表的文件,如果是本地需要add file,如果是hdfs不需add file可直接读取
第四个参数 keyfields 关联数据表中关联字段列表,用逗号,连接
第五个参数 valuefields 关联数据表要输出的字段列表,用逗号,连接
第六个参数 innerorouter 取值为inner时为inner join,取值为outer时为outer join>,outer join时hive数据表中每个记录都有输出
第七个参数 defaultvalue 当outer join时如果关联数据表中没有取值用的缺省值
之后的参加tabledefinefile只在一个文件中定义,该参数不需重复,再增加6个参数可>用于关联多个文件
memjoinindex读入数据文件到内存,与hive中的数据表进行join,需要6个参数,如果关联>多个文件则6x个参数
与memjoin实现同样功能,参数用的是索引号,从0开始计
没有tabledefinefile参数,6个参数与memjoin后面6个参数相同,字段名称全部改为字>段索引号
使用例子:
select *
from
(
MAP id,fielda1,fielda2
USING './forhive.py memjoin tabledef.txt id hdfs:/tableb/* id fieldb1,fieldb2 outer ,0,0 '
AS id,fielda1,fielda2,
fieldb1,fieldb2
from (
select *
from tablea
) a
) a;
python源码:
#!/usr/bin/python
#coding:utf8
import sys
import subprocess
#用于hive中处理数据的python函数集
#本python包主要完成基于内存的join
#分隔符
parametersep=","
valuesep="\t"
keyvaluesep=" : "
#初始化
def initMapValues(joinindex, filepath, keyindex, valueindex, innerorouter, defaultvalue, others):
joinnumber = 0
joinindexs = []
joindicts = []
innerorouters = []
defaults = []
while(True):
joinindexs.append( [int(index) for index in joinindex.split(parametersep)] )
joindicts.append( readFileToDict(filepath,keyindex,valueindex) )
if(innerorouter != "inner" and innerorouter != "outer"):
raise Exception, "error: innerorouter parameter error, must be inner or outer"
innerorouters.append( innerorouter == "outer" )
defaults.append( defaultvalue.split(parametersep) )
joinnumber += 1
if(len(others) <= 0):
break
if(len(others) < 6 ):
raise Exception, "error: the number of parameters must be 6 multiple"
joinindex, filepath, keyindex, valueindex, innerorouter, defaultvalue = others[:6]
others = others[6:]
return (joinnumber,joinindexs,joindicts,innerorouters,defaults)
#读文件到内存,之后处理标准输入的每行记录,内存表是要关联表的外键表,可以是多个字段的外键
#joinindex为标准输入的字段索引号,多个字段索引号的用逗号,分隔
#filepath是要读入内存的文件路径
#keyindex是文件中用于join的key字段索引号,多个字段索引号的用逗号,分隔,应与joinindex索引个数相同
#valueindex是文件中join后提取的字段索引号,多个字段索引号的用逗号,分隔
#innerorouter "inner"为inner join,"outer"为outer join
#defaultvalue是如果是outer join,对于没有外键提供的缺省值
#others为支持多个表同时join,其他表的joinindex,filepath,keyindex,valueindex,innerorouter,defaultvalue,...
def mapValueByIndex(args):
joinnumber,joinindexs,joindicts,isouters,defaults = initMapValues(args[0], args[1], args[2], args[3], args[4], args[5], args[6:])
for line in sys.stdin:
line = line.strip()
fields = line.split(valuesep)
output = fields
isprint = True
for index in range(joinnumber) :
value = [ fields[i] for i in joinindexs[index] ]
getvalue = joindicts[index].get(valuesep.join(value))
if( not getvalue ):
if( not isouters[index] ):
isprint = False
break;
else:
getvalue = defaults[index]
output += getvalue
if(isprint):
print(valuesep.join(output))
#从文件中读取数据表,生成用于表关联的字典
#输入:
# filename 保存数据的文件名
# keyindex key字段索引号,多个字段索引号用逗号分隔
# valueindex value字段索引号,多个字段索引号用逗号分隔
#返回:
# 用于表关联的字典
def readFileToDict( filename, keyindex, valueindex ):
result = {}
keyindex = [ int(index) for index in keyindex.split(parametersep) ]
valueindex = [ int(index) for index in valueindex.split(parametersep) ]
if(filename[:5] == "hdfs:"):
file = subprocess.Popen(["hadoop", "fs", "-cat", filename], stdout=subprocess.PIPE)
else:
file = subprocess.Popen(["cat", filename], stdout=subprocess.PIPE)
#file = open(filename,"r")
for line in file.stdout:
#line = file.readline()
line = line.strip()
fields = line.split(valuesep)
keys = [ fields[i] for i in keyindex ]
values = [ fields[i] for i in valueindex ]
result[valuesep.join(keys)] = values
return result
#根据字段名称生成字段名称到索引号的字典
#输入:各字段用逗号分隔,
#返回字段名称到索引号的字典
def nameindex(namestr):
names = [ name.strip() for name in namestr.split(",") ]
namenum = len(names)
return dict(zip(names,range(namenum)))
#从文件中读取表定义的字段名称
def getTableField(filename):
tablefields = []
if(filename[:5] == "hdfs:"):
file = subprocess.Popen(["hadoop", "fs", "-cat", filename], stdout=subprocess.PIPE)
else:
file = subprocess.Popen(["cat", filename], stdout=subprocess.PIPE)
#file = open(filename,"r")
for line in file.stdout:
#line = file.readline()
line = line.strip()
splitted = line.split(keyvaluesep)
if(len(splitted) != 2):
continue
tablename, fieldnames = splitted
fieldindex = nameindex(fieldnames)
tablefields.append( {"name":tablename.strip(),"index":fieldindex,"fields":fieldnames} )
return tablefields
#根据表定义,把字段名称转化为字段索引
def getFieldIndex(tablefields, filename, fieldnames ):
tabledef = []
for tabledef in tablefields:
if(filename.find(tabledef["name"]) >= 0):
break
fieldnames = fieldnames.split(parametersep)
fieldindex = [ tabledef["index"][name] for name in fieldnames ]
if( tabledef ):
return parametersep.join( [ str(index) for index in fieldindex ] )
else:
raise Exception, "not fould table define for " + filename
#读文件到内存,之后处理标准输入的每行记录,内存表是要关联表的外键表,可以是多个字段的外键
def mapValue(args):
tablefields = getTableField(args[0])
indexargs = args[1:]
i = 0
while( i < len(args[1:]) ):
indexargs[i] = getFieldIndex(tablefields, "stdin", args[i+1])
indexargs[i+2] = getFieldIndex(tablefields, args[i+2], args[i+3])
indexargs[i+3] = getFieldIndex(tablefields, args[i+2], args[i+4])
i += 6
mapValueByIndex(indexargs)
#函数字典
functions = {
"memjoin" : mapValue,
"memjoinindex" : mapValueByIndex
}
#主函数入口
def main(args):
func = functions.get(args[1])
if(func):
func(args[2:])
else:
raise Exception, "has no function " + args[1]
#打印用法
def printUsage():
print("usage:")
print(" ./forhive.py CMD parameters")
print(" CMD :")
print(" \t memjoin tabledefinefile joinfields filepath keyfields valuefields innerorouter defaultvalue ...")
print(" \t memjoinindex joinindex filepath keyindex valueindex innerorouter defaultvalue ...")
if __name__ == '__main__':
if(len(sys.argv) > 1):
main(sys.argv)
else:
printUsage()
tabledef.txt
stdin : id,fielda1,fielda2
tableb : fieldb1,fieldb2