• hive中concat_ws的秘密


    hive中concat_ws的秘密

    1. 背景

    在hive中,concat_ws有两种用法:

    concat_ws("|", array('a', 'b')); 
    -- 输出a|b
    concat_ws('|', 'a', 'b');
    -- 输出a|b
    
    • 1
    • 2
    • 3
    • 4

    其中,第一个参数是其它字符串的分隔符,concat_ws有个比较好用的特点,就是会自动跳过值为null的字符串(注意:空字符串不会跳过)。例如:

    concat_ws('|', 'a', null, 'b');
    -- 输出a|b
    
    • 1
    • 2

    但是在工作中,我遇到了一个奇怪的问题:

    concat_ws('|', 'a', null, 'b');
    -- 输出a|b
    concat_ws('|', array('a', null, 'b'));
    -- 输出a|null|b
    
    • 1
    • 2
    • 3
    • 4

    这就奇怪了,不是说concat_ws会忽略所有null吗,为什么此非但没遇到,还输出了“null”字符串?

    2. 源码探究

    2.1 concat_ws源码

    实在按奈不住好奇心,于是去扒了一下concat_ws的UDF源码:

    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
        if (arguments[0].get() == null) {
            return null;
        }
        // 获取分隔符
        String separator = PrimitiveObjectInspectorUtils.getString(
            arguments[0].get(), (PrimitiveObjectInspector)argumentOIs[0]);
    
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (int i = 1; i < arguments.length; i++) {
            // 判断所取的值是不是null
            if (arguments[i].get() != null) {
                // 如果是第一个字符串,则不加分隔符,否则加
                if (first) {
                    first = false;
                } else {
                    sb.append(separator);
                }
                // 如果第二个参数是List,也就是 concat_ws('|', array()) 这种使用类型
                if (argumentOIs[i].getCategory().equals(Category.LIST)) {
                    Object strArray = arguments[i].get();
                    ListObjectInspector strArrayOI = (ListObjectInspector) argumentOIs[i];
                    boolean strArrayFirst = true;
                    for (int j = 0; j < strArrayOI.getListLength(strArray); j++) {
                        // 与外循环同样的处理逻辑,唯一的区别就是没有判断null
                        if (strArrayFirst) {
                            strArrayFirst = false;
                        } else {
                            sb.append(separator);
                        }
                        sb.append(strArrayOI.getListElement(strArray, j));
                    }
                } else {
                    // 如果第二个参数不是List,则一个一个append,中间穿插
                    sb.append(PrimitiveObjectInspectorUtils.getString(
                        arguments[i].get(), (PrimitiveObjectInspector)argumentOIs[i]));
                }
            }
        }
        resultText.set(sb.toString());
        return resultText;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里,可以清晰的看到concat_ws的处理流程。在取每个参数值后,会有个arguments[i].get() != null,判断是否为null。如果所取的参数类型为array,就进入for循环,取出array中的每个值。

    与外层循环不同的是,array中的循环并没有做null判断处理,而是直接append。

    因此,如果使用concat_ws('|', array())这种模式,array中的null值并不会被跳过。

    但是至此还有一个疑问,最后输出结果中的null字符串是哪里来的呢?

    2.2 stringbuilder源码

    终于到了最后的阶段,打开stringbuilder的源码,如果append进来的String为null,最后会走到appendNull方法。

    image-20220630012546951

    private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    至此,终于搞清楚了concat_ws中null字符串的来源。

  • 相关阅读:
    Source Insight 宏-添加单行的c注释
    python导出requirements.txt的几种方法及环境配置流程
    求和、计数的窗口函数应用
    郑泽康:一名热爱技术的“保安”
    Spark - 第3章 Spark工作集介绍
    STM32物联网项目-SHT30温湿度采集(IIC通信)
    机器人控制算法八之路径规划算法:RRT、RRT-Connect、Dynamic-Domain RRTs*
    从零开始Blazor Server(9)--修改Layout
    《逆向工程核心原理》学习笔记(七):反调试技术
    嵌入式养成计划-46----QT--简易版网络聊天室实现--QT如何连接数据库
  • 原文地址:https://blog.csdn.net/u012861792/article/details/125532456