• 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字符串的来源。

  • 相关阅读:
    注册树模式
    AWS云服务器EC2实例实现ByConity快速部署
    在虚拟机安装JDK
    Meta通过开源Llama 3 LLM提高了标准
    设置Ubuntu 20.04的静态IP地址(wifi模式下)
    file2Udp增量日志转出Udp简介
    第7章 - 多无人机系统的协同控制 --> 无人机模型分析
    【数据结构】顺序查找,折半查找,分块查找的知识点总结及相应的代码实现
    Java基础:Java基本概念
    知识图谱从入门到应用——知识图谱的知识表示:向量表示方法
  • 原文地址:https://blog.csdn.net/u012861792/article/details/125532456