testbook

用户自定义函数

    Inceptor支持用户自定义函数,本节将首先举例说明如何自定义函数并应用在Inceptor里,之后将列举Inceptor自增的UDF

  • 创建自定义函数(UDF)

    自定义UDF时需要注意以下几点:
    1.自定义UDF需要继承org.apache.hadoop.hive.ql.exec.UDF
    2.需要实现evaluate函数
    3.evaluate函数支持重载

    以下代码实现了两个数求和的UDF,evaluate函数代表两个整型数据相加或两个浮点型数据相加

    package inceptor.udf;
    import org.apache.hadoop.hive.ql.exec.UDF;
    
    public final class Add extends UDF {
       public Integer evaluate(Integer a, Integer b) {
         if (null == a || null == b) {
            return null;
         }
         return a + b;
       }
    
       public Double evaluate(Double a, Double b) {
         if (a == null || b == null)
            return null;
         return a + b;
       }
    }
    

    实际使用时,需要执行以下几步:
    1.程序打包放到目标机器上进入Inceptor客户端,添加jar包:add jar [jar_file_path];
    2.创建临时函数:CREATE TEMPORARY FUNCTION my_add AS 'inceptor.udf.Add';
    3.使用临时函数:SELECT my_add(scores.math, scores.art) FROM scores;
    4.使用完成后销毁临时函数:DROP TEMPORARY FUNCTION my_add;

  • 创建自定义聚合函数(UDAF)

    自定义UDAF时需要注意以下几点:
    1.自定义UDAF需要继承org.apache.hadoop.hive.ql.exec.UDAF
    2.内部类Evaluator需要实现UDAFEvaluator接口:Evaluator需要实现init、iterate、terminatePartial、merge、terminate这几个函数
    2.1.init函数实现接口UDAFEvaluator的init函数
    2.2.iterate接收传入的参数,并进行内部的轮转,其返回类型为boolean
    2.3.terminatePartial无参数,其为iterate函数轮转结束后,返回轮转数据,terminatePartial类似于hadoop的Combiner
    2.4.merge接收terminatePartial的返回结果,进行数据merge操作,其返回类型为boolean
    2.5.terminate返回最终的聚集函数结果

    以下代码实现了一个求平均数的UDAF

    package inceptor.udaf;
    import org.apache.hadoop.hive.ql.exec.UDAF;
    import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
    
    public class Avg extends UDAF {
       public static class AvgState {
         private long mCount;
         private double mSum;
       }
    
       public static class AvgEvaluator implements UDAFEvaluator {
         AvgState state;
    
         public AvgEvaluator() {
            super();
            state = new AvgState();
            init();
         }
    
         public void init() {
            state.mSum = 0;
            state.mCount = 0;
         }
    
         public boolean iterate(Double o) {
            if (o != null) {
              state.mSum += o;
              state.mCount++;
            }
            return true;
         }
    
         public AvgState terminatePartial() {
            return state.mCount == 0 ? null : state;
         }
    
         public boolean merge(AvgState o) {
            if (o != null) {
              state.mCount += o.mCount;
              state.mSum += o.mSum;
            }
            return true;
         }
    
         public Double terminate() {
            return state.mCount == 0 ? null : Double.valueOf(state.mSum
                 / state.mCount);
         }
       }
    }
    

    实际使用时,需要执行以下几步:
    1.程序打包放到目标机器上进入Inceptor客户端,添加jar包:add jar [jar_file_path];
    2.创建临时函数:CREATE TEMPORARY FUNCTION my_avg AS 'inceptor.udaf.Avg';
    3.使用临时函数:SELECT scores.art, my_avg(scores.math) FROM scores GROUP BY scores.art;
    4.使用完成后销毁临时函数:DROP TEMPORARY FUNCTION my_avg;

  • 下面列举Inceptor自增的UDF

  • trunc
    trunc(date, format):支持string类型的date字段按format返回特定的日期string
    trunc(number,num_digits):支持数字类型的按给定数值进行裁剪
    
  • to_char
    to_char(date, pattern):把yyyy-MM-dd HH:mm:ss形式的string转化为输入pattern形式的string
    to_char(datetime, pattern):把yyyy-MM-dd形式的string转化为输入pattern形式的string
    to_char(number [,format]):把数值转化为string,基本类型(byte, short, int, bigint, float, double)转string或按照一定format转string
    
  • translate
    translate('input_string','from_string','to_string')
    

    translate方法作用于input string(第一个参数)的字符上,规则为出现在from_string中的字符用to_string中对应位置的字符来替换。如果to_string的长度比from_string短,那么from_string中多出来的位置的字符会在output中被移除。

    translate('abcdef', 'adc', '19'):返回 '1b9ef','1'代替'a','9'代替'd','c'被移除。
    
  • nvl
    nvl(eExpr1, eExpr2):eExpr1为空时设为eExpr2的值,仅支持基本类型(包括string)
    
  • nvl2
    nvl2(eExpr1, eExpr2, eExpr3):eExpr1不为空时设置为eExpr2的值,为空时设置为eExpr3的值,仅支持基本类型(包括string)
    
  • dense_rank
    dense_rank(hash_key, order_by_col1, order_by_col2 ...):dense_rank与rank的区别是dense_rank的排序号会持续+1递增的,不会由于部分字段相同排序号而跳跃,即按照1、2、2、2、3、4、5排序
    select key1,key2,value,dense_rank(1,value) as rank from (select key1,key2,value from test_table distribute by hash(key1) sort by key1,key2,value) t;
    
  • greatest
    greatest(value1, value2, value3, ....):字段属性必须为PRIMITIVE,且需要是相同的TypeInfo,或者能转成CommonBase
    
  • group_max
    select key1,key2,value,group_max(key1,key2) as rank from (select key1,key2,value from test_table sort by key1) t;
    

    NOTE:按照key1 group by之后对key2取其中的最大值,此方法不属于聚合函数,所以是逐行处理的。由于每个group内当前行记录只是基于其之前所有行的统计结果(而不是扫描group内所有行),所以每个group内前面部分行记录的最大值可能不是group真正的最大值:

    1    12    string1    12
    1    2     string1    12
    1    11    string1    12
    1    12    string2    12
    2    NULL  string1    12
    2    -21   string1    -21
    2    23    string1    23
    2    4     string2    23
    2    14    string2    23
    3    32    string2    32
    3    6     null       32
    4    8                 8
    4    42    string2    42
    5    54    string2    54
    5    52    string1    54
    5    NULL  value5     54
    6    12    502        12
    6    64    string2    64
    

    类似的函数还有group_min和group_sum

  • instr

    instr(str, substr):查找子字符串index
    
  • lag
    lag(hash_key,column[,offset[,default]]):用法和group_max等类似依赖于sort_by。此函数和oracle原生语法相同,取出当前行前面第N行的数据,其不支持取出后N行数据,暂时也没有其他类似方法提供该功能
    

    注意事项如下:
    1.column与default值需要是同一字段类型
    2.offset只能为自然数,其默认值为1,即取前一行的字段数据,如果没有则为NULL,如果设为负数将全为NULL

    范例:

    select key1,key2,value,lag(key1,key2) as rank from (select key1,key2,value from test_table distribute by key1 sort by key1,value) t
    
    1    11    string1    NULL
    1    2     string1    11
    1    12    string1    2
    1    12    string2    12
    2    NULL  string1    NULL
    2    -21   string1    NULL
    2    23    string1    -21
    2    14    string2    23
    2    4     string2    14
    3    6     null       NULL
    3    32    string2    6
    4    8                NULL
    4    42    string2    8
    5    52    string1    NULL
    5    54    string2    52
    5    NULL  value5     54
    6    12    502        NULL
    6    64    string2    12
    
  • lnnvl
    lnnvl(condition):当条件为true时返回false
    
  • rank
    rank(hash_key, order_by_col1, order_by_col2 ...):与dense_rank类似,但是其会以1、2、2、2、5、6、7排序
    
  • row_number
    row_number(hashkey)
    select key1,key2,value,row_number(key1) as rank from (select key1,key2,value from test_table distribute by key1 sort by key1) t;
    

    此函数同样不属于聚合函数,可看做rank的一个变种,给出1、2、3、4的标准行号,因此不需要比较部分字段是否相同以停留排序号。其依然依赖于distribute by和sort by之后的group结果,查询结果如下:

    1    12    string1    1
    1    2     string1    2
    1    11    string1    3
    1    12    string2    4
    2    NULL  string1    1
    2    -21   string1    2
    2    23    string1    3
    2    14    string2    4
    2    4     string2    5
    3    32    string2    1
    3    6     null       2
    4    8                1
    4    42    string2    2
    5    54    string2    1
    5    52    string1    2
    5    NULL  value5     3
    6    64    string2    1
    6    12    502        2
    
  • to_number
    to_number(value, format_mask):string类型转换为number,按指定的format
    

    NOTE:1.value必须为string类型
    2.无参数时转为整数
    3.format_mask必须为double或者字符串(字符串必须为带“$”且value也带“$”,否则抛出异常并返回NULL结果,但可以执行完成)
    4.format_mask必须全由9组成,否则将返回空
    5.当format_mask为double时,不会影响其结果的小数点位数(最多给整数加上.0)

  • chr
    chr(number_code):只能传入int,转为String.valueof((char) number_code),字符编码格式估计为内置的unicode
    
  • str_to_date
    str_to_date(dateText,pattern):将输入的string类型的date按照pattern转为string类型的date,但是一定要以“yyyy-MM-dd HH:mm:ss”格式返回最终结果
    
  • date_format
    date_format(dateText,pattern):将输入的string类型的date按照pattern转为string类型的date,以pattern格式返回最终结果,这是其与str_to_date的区别
    
  • WITH AS
    WITH DEPT_COSTS AS--查询出部门的总工资
     (SELECT D.DNAME, SUM(E.SAL) DEPT_TOTAL
    FROM DEPT D, EMP E
    WHERE E.DEPTNO = D.DEPTNO
    GROUP BY D.DNAME),
    AVE_COST AS --查询出部门的平均工资,在后一个WITH语句中可以引用前一个定义的WITH语句
     (SELECT SUM(DEPT_TOTAL) / COUNT() AVG_SUM FROM DEPT_COSTS)
    SELECT 
    FROM DEPT_COSTS DC
    WHERE DC.DEPT_TOTAL >
      (SELECT AC.AVG_SUM FROM AVE_COST AC) --进行比较
    
  • 窗口聚合函数 OVER子句:
    SELECT SalesOrderID, CustomerID,OrderDate, TotalDue,
    SUM(TotalDue) OVER (PARTITION BY CustomerID) AS CustomerTotal,
    SUM(TotalDue) OVER() AS GrandTotal,
    AVG(TotalDue) OVER (PARTITION BY CustomerID) AS AvgCustSale
    FROM Sales.SalesOrderHeader OuterQuery
    ORDER BY CustomerID;
    
  • GROUP BY扩展 Rollup 生成简单的 GROUP BY 聚合行以及小计行或超聚合行,还生成一个总计行
    SELECT a, b, c, SUM (  ) FROM T GROUP BY ROLLUP (a,b,c);  会为(a, b, c)、(a, b)和(a)值的每个唯一组合生成一个带有小计的行。还将计算一个总计行。
    
    Cube生成简单的 GROUP BY 聚合行、ROLLUP 超聚合行和交叉表格行
    SELECT a, b, c, SUM () FROM T GROUP BY CUBE (a,b,c);
    会为(a, b, c)、(a, b)、(a, c)、(b, c)、(a)、(b)和(c)值的每个唯一组合生成一个带有小计的行,还会生成一个总计行。
    
  • INTERSECT
  • MINUS/EXCEPT
  • EXISTS, NOT EXISTS
  • >ALL, < ALL, >ANY, < ANY, =ANY
  • INSERT 扩展
    插入单条数据
    insert into TABLE_NAME(col1, col2, col3) values(value1, value2, value3);
    
    插入多条数据
    insert into table TABLE_NAME select value1, value2, value3 from SRC_TABLE;
    
    指定插入的字段数量及顺序
    insert into table TABLE_NAME(col2, col1) select value2, value1 from SRC_TABLE;
    
  • UPDATE扩展
    常量更新
    update TABLE_NAME set col1='value1', col2='value2' where rk like '123%';
    
    子查询更新
    update TABLE_NAME set (col1, col2) = (select 'value1', 'value2' from TABLE_NAME where rk like '123%');
    
  • DELETE扩展:
    删除表数据:
    delete from TABLE_NAME where rk like '123%';