hive运算时类型自动转化问题
创始人
2025-05-31 04:42:48

比如table  id int ,name string 

我们可以

select * from table where id='1'

select * from table where name=1

select * from table where name=cast(1 as decimal)

这些都不会报错,因为涉及到了类型的自动转化。

但是 当我们有 nvl(1,'1')时 结果类型时什么样呢? 研究下。

以 +好运算符为例。

打开hive源码 发现两个类

GenericUDFOPPlus

GenericUDFOPNumericPlus

一时间我还分不清我们平常使用 +好运算符是哪个类 找了半天发现是 

GenericUDFBaseNumeric

@Override
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {if (arguments.length != 2) {throw new UDFArgumentException(opName + " requires two arguments.");}for (int i = 0; i < 2; i++) {Category category = arguments[i].getCategory();if (category != Category.PRIMITIVE) { --只能是基础的类型throw new UDFArgumentTypeException(i, "The "+ GenericUDFUtils.getOrdinal(i + 1)+ " argument of " + opName + "  is expected to a "+ Category.PRIMITIVE.toString().toLowerCase() + " type, but "+ category.toString().toLowerCase() + " is found");}}// During map/reduce tasks, there may not be a valid HiveConf from the SessionState.// So lookup and save any needed conf information during query compilation in the Hive conf// (where there should be valid HiveConf from SessionState).  Plan serialization will ensure// we have access to these values in the map/reduce tasks.if (confLookupNeeded) {CompatLevel compatLevel = HiveCompat.getCompatLevel(SessionState.get().getConf());ansiSqlArithmetic = compatLevel.ordinal() > CompatLevel.HIVE_0_12.ordinal();confLookupNeeded = false;}--获取左右两边的值leftOI = (PrimitiveObjectInspector) arguments[0];rightOI = (PrimitiveObjectInspector) arguments[1];--获取结果类型 继续看这个方法resultOI = PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(deriveResultTypeInfo());converterLeft = ObjectInspectorConverters.getConverter(leftOI, resultOI);converterRight = ObjectInspectorConverters.getConverter(rightOI, resultOI);--注意这里 是根据resultOi取将leftOi和rightOi转化为这个类型return resultOI;
}

deriveResultTypeInfo

private PrimitiveTypeInfo deriveResultTypeInfo() throws UDFArgumentException {PrimitiveTypeInfo left = (PrimitiveTypeInfo) TypeInfoUtils.getTypeInfoFromObjectInspector(leftOI);--左边是什么类型PrimitiveTypeInfo right = (PrimitiveTypeInfo) TypeInfoUtils.getTypeInfoFromObjectInspector(rightOI);--右边是什么类型if (!FunctionRegistry.isNumericType(left) || !FunctionRegistry.isNumericType(right)) { --如果左右两边有个不是number类型就报错List argTypeInfos = new ArrayList(2);argTypeInfos.add(left);argTypeInfos.add(right);throw new NoMatchingMethodException(this.getClass(), argTypeInfos, null);}
--注意这里说了据 如果左右两边有个不准确的类型(str,dou,float) 就是resultOI=double// If any of the type isn't exact, double is chosen.if (!FunctionRegistry.isExactNumericType(left) || !FunctionRegistry.isExactNumericType(right)) {
--这个方法是说 如果左右两边有string 我们先转化为double
--如果有个null 我们就返回另外一个类型。也就是double或float(string上一步已经是double)
--如果此时 是float 和double 那就看谁的level高 可以看到float高于doublereturn deriveResultApproxTypeInfo();}
--这里就是准确类型相加 一般是int decimal long
--首先看有无null 有null就取另外一遍类型。
--再看类型相等就取同样的类型
--再看类型级别。byte>short>int>long>decimal 。不一样取 高级别return deriveResultExactTypeInfo();
}

deriveResultApproxTypeInfo

protected PrimitiveTypeInfo deriveResultApproxTypeInfo() {PrimitiveTypeInfo left = (PrimitiveTypeInfo) TypeInfoUtils.getTypeInfoFromObjectInspector(leftOI);PrimitiveTypeInfo right = (PrimitiveTypeInfo) TypeInfoUtils.getTypeInfoFromObjectInspector(rightOI);// string types get converted to doubleif (PrimitiveObjectInspectorUtils.getPrimitiveGrouping(left.getPrimitiveCategory())== PrimitiveGrouping.STRING_GROUP) {left = TypeInfoFactory.doubleTypeInfo;}if (PrimitiveObjectInspectorUtils.getPrimitiveGrouping(right.getPrimitiveCategory())== PrimitiveGrouping.STRING_GROUP) {right = TypeInfoFactory.doubleTypeInfo;}    // Use type promotionPrimitiveCategory commonCat = FunctionRegistry.getPrimitiveCommonCategory(left, right);if (commonCat == PrimitiveCategory.DECIMAL) {// Hive 0.12 behavior where double * decimal -> decimal is gone.return TypeInfoFactory.doubleTypeInfo;} else if (commonCat == null) {return TypeInfoFactory.doubleTypeInfo;} else {return left.getPrimitiveCategory() == commonCat ? left : right;}
}

 

deriveResultExactTypeInfo

/*** Default implementation for getting the exact type info for the operator result. It worked for all* but divide operator.** @return*/
protected PrimitiveTypeInfo deriveResultExactTypeInfo() {PrimitiveTypeInfo left = (PrimitiveTypeInfo) TypeInfoUtils.getTypeInfoFromObjectInspector(leftOI);PrimitiveTypeInfo right = (PrimitiveTypeInfo) TypeInfoUtils.getTypeInfoFromObjectInspector(rightOI);// Now we are handling exact types. Base implementation handles type promotion.PrimitiveCategory commonCat = FunctionRegistry.getPrimitiveCommonCategory(left, right);if (commonCat == PrimitiveCategory.DECIMAL) {return deriveResultDecimalTypeInfo();} else {return left.getPrimitiveCategory() == commonCat ? left : right;}
}

 

deriveResultDecimalTypeInfo
/*** Derive the object inspector instance for the decimal result of the operator.*/
protected DecimalTypeInfo deriveResultDecimalTypeInfo() {int prec1 = leftOI.precision();int prec2 = rightOI.precision();int scale1 = leftOI.scale();int scale2 = rightOI.scale();return deriveResultDecimalTypeInfo(prec1, scale1, prec2, scale2);
}

 

 计算精度 可以详见我之前的文章。hive3.1decimal计算详细逻辑_hive decimal_cclovezbf的博客-CSDN博客

public static PrimitiveCategory getPrimitiveCommonCategory(TypeInfo a, TypeInfo b) {if (a.getCategory() != Category.PRIMITIVE || b.getCategory() != Category.PRIMITIVE) {return null;}--PrimitiveCategory pcA = ((PrimitiveTypeInfo)a).getPrimitiveCategory();PrimitiveCategory pcB = ((PrimitiveTypeInfo)b).getPrimitiveCategory();
--先看是不是都是基本类型if (pcA == pcB) {// Same primitive categoryreturn pcA;}if (pcA == PrimitiveCategory.VOID) {// Handle NULL, we return the type of pcBreturn pcB;}if (pcB == PrimitiveCategory.VOID) {// Handle NULL, we return the type of pcAreturn pcA;}PrimitiveGrouping pgA = PrimitiveObjectInspectorUtils.getPrimitiveGrouping(pcA);PrimitiveGrouping pgB = PrimitiveObjectInspectorUtils.getPrimitiveGrouping(pcB);
--看看左右类型是不是同一个组 int/long/decimal/float/doub 都是number组
--string varchar 属于string组if (pgA == pgB) {// Equal groups, return what we can handleswitch (pgA) {case NUMERIC_GROUP: {Integer ai = TypeInfoUtils.numericTypes.get(pcA);Integer bi = TypeInfoUtils.numericTypes.get(pcB);return (ai > bi) ? pcA : pcB; --这里就看level了}case DATE_GROUP: {Integer ai = TypeInfoUtils.dateTypes.get(pcA);Integer bi = TypeInfoUtils.dateTypes.get(pcB);return (ai > bi) ? pcA : pcB;}case STRING_GROUP: {// handle string types properlyreturn PrimitiveCategory.STRING;}default:break;}}

 isNumericType long decimal float double string varchar 都算number类型

public static boolean isNumericType(PrimitiveTypeInfo typeInfo) {switch (typeInfo.getPrimitiveCategory()) {case BYTE:case SHORT:case INT:case LONG:case DECIMAL:case FLOAT:case DOUBLE:case STRING: // String or string equivalent is considered numeric when used in arithmetic operator.  -- 这里说了string在 op 运算时可以看做 numbercase VARCHAR:case CHAR:case VOID: // NULL is considered numeric type for arithmetic operators.return true;default:return false;}
}

 isExactNumericType

/*** Check if a type is exact (not approximate such as float and double). String is considered as* double, thus not exact.*  这里说 float double string 都不准确。前两个精度丢失,string 是不确定。* @param typeInfo* @return*/
public static boolean isExactNumericType(PrimitiveTypeInfo typeInfo) {switch (typeInfo.getPrimitiveCategory()) {case BYTE:case SHORT:case INT:case LONG:case DECIMAL:return true;default:return false;}
}

group的概念

case BOOLEAN:return PrimitiveObjectInspectorUtils.PrimitiveGrouping.BOOLEAN_GROUP;
case BYTE:
case SHORT:
case INT:
case LONG:
case FLOAT:
case DOUBLE:
case DECIMAL:return PrimitiveObjectInspectorUtils.PrimitiveGrouping.NUMERIC_GROUP;
case STRING:
case CHAR:
case VARCHAR:return PrimitiveObjectInspectorUtils.PrimitiveGrouping.STRING_GROUP;
case DATE:
case TIMESTAMP:
case TIMESTAMPLOCALTZ:return PrimitiveObjectInspectorUtils.PrimitiveGrouping.DATE_GROUP;
case INTERVAL_YEAR_MONTH:
case INTERVAL_DAY_TIME:return PrimitiveObjectInspectorUtils.PrimitiveGrouping.INTERVAL_GROUP;
case BINARY:return PrimitiveObjectInspectorUtils.PrimitiveGrouping.BINARY_GROUP;
case VOID:return PrimitiveObjectInspectorUtils.PrimitiveGrouping.VOID_GROUP;
default:return PrimitiveObjectInspectorUtils.PrimitiveGrouping.UNKNOWN_GROUP;
}

level

registerNumericType(PrimitiveCategory.BYTE, 1);
registerNumericType(PrimitiveCategory.SHORT, 2);
registerNumericType(PrimitiveCategory.INT, 3);
registerNumericType(PrimitiveCategory.LONG, 4);
registerNumericType(PrimitiveCategory.DECIMAL, 5);
registerNumericType(PrimitiveCategory.FLOAT, 6);
registerNumericType(PrimitiveCategory.DOUBLE, 7);
registerNumericType(PrimitiveCategory.STRING, 8);

到这里我们总结下。 int long float double decimal string void 。(byte varchar char date暂时不考虑)。

1先看左右两边又没有不准确的类型(float double string void)。

        1.1如果哪边有类型属于group_string,那么这个category->category.double

        1.2左右两边全都是int double dec,属于精确数字计算

2.再比较左右两边类型

        2.1 category_left或者category_right有void,那就返回另外一边类型,不用比较group

        2.2 category_left == category_right ,那就返回这个类型,不用比较group

        2.3 category_left != category_right 看他们属于哪个group (number_group,string_group)。

        再比较左右两边的group

        2.3.1group_left == group_right,

                2.3.1.1如果是number_group 那么就看谁的level高。(string>double>floatdec>long>int)

                2.3.1.2如果是string_group 那么就返回commonCatory(double/float)

        2.3.2 group_left != group_right

                2.3.2.1此时一般都是 一边是group_number 一边是group_string,那就返回group_string的commonCatory(double float)

               2.3.2.2 如果一边为null一边为group_number 返回commonCatory=null

3. 根据左右两边group 和category比较后返回的类型commonCatory

        3.1  如果你是1.1 非精确数字计算

                3.1.1 最后两个比较后的resultOi=catetoy_decimal 就返回 double(// Hive 0.12 behavior where double * decimal -> decimal is gone.)

               3.1.2commonCatory=null 返回 double,

                3.1.3commonCatory前两个,那么就看你的返回类型和leftOi是否一样,一样就返回left 否则返回rightOi

        3.2 如果你是 精确数字计算

                3.2.1 返回结果是 decimla 还需要计算 看第5步

                3.2.1返回类型不是dec,那么就看你的返回类型和leftOi是否一样,一样就返回left 否则返回rightOi

4.如果是4.2.1 也就是准确数字计算,resultOi 返回decimal,还需要将leftOi和rigthOi转化一下。然后再运算

好了直接实战。

 select str + na,        -- double null   
       str + integ,     -- double 2        
       str + lon,       -- double 2
       str + doub,      -- double 2
       str + floa,      -- double 2.200000047683716
       str + dec1,      -- double 2
       str + dec2,      -- double 2
       na + integ,      -- integer null
       na + lon,        -- bigint null
       na + doub,       -- double null
       na + floa,       -- float null
       na + dec1,       -- double null
       na + dec2,       -- double null
       lon + doub,      -- double 2
       lon + floa,      -- float 2.2
       lon + dec1,      -- decimal(38,17) 2.00000000000000000
       lon + dec1,      -- decimal(38,17) 2.00000000000000000
       doub + floa,     -- double 2.200000047683716
       doub + dec1,     -- double 2
       doub + dec2,     -- double 2
       dec1 + dec2      -- decimal(38.17) 2.00000000000000000
from (
         select 1                          as integ,
                1L                         as lon,
                float(1.2)                 as floa,
                '1'                        as str,
                double(1.0)                as doub,
                cast(1 as decimal(38, 18)) as dec1,
                cast(1 as decimal(10, 5)) as dec2,
                null                       as na
     ) t

先详细解释下几个

string +na ,string根据 1.1 转化为double ,根据2.1返回double

string + float, string根据1.1转化为double,根据 2.3接着2.3.1.1,此时dou和floa都是number_group,判断level double高,返回double,

这里简单来说,string+任何其他数字 都是double。

na+dec ,走2.1返回decimal 走 3.1非精确计算,走3.1.1返回double

累了。。。反正 第四步decimla转化和后面计算有点累了 不看了。

相关内容

热门资讯

今年我省粮食产量达515.56... (来源:辽宁日报)转自:辽宁日报 图为在中储粮(盘锦)储运有限公司,装运粮食的重型卡车排起长队...
国家发展改革委部署促进投资止跌... (来源:辽宁日报)转自:辽宁日报 新华社北京12月13日电 (记者魏玉坤) 记者13日从全国发展和改...
江苏省实施《中华人民共和国森林... (来源:新华日报) 目 录 第一章 总则 第二章 森林、林木和林地权属管理...
姜堰数字化产品讲“活”理论 (来源:新华日报) □ 本报记者 卢佳乐 通讯员 姜宣 “王教授,您约我‘喝茶论道’,...
联合国维和部队在苏丹遇袭 6人... 转自:财联社【联合国维和部队在苏丹遇袭 6人死亡】财联社12月14日电,当地时间13日,苏丹武装部队...