HIVE如何校验map数据


内容目录

  • HIVE如何校验map数据
  • 一、描述
  • 二、思路
  • 三、实现:
  • 实现方法1:
  • 实现方法2
  • 改进


在数据校验中,一般会进行checksum,即比较变换前后的hash值的和是否相同,再不考虑哈希碰撞的特别情况下,如果checksum的结果一致,就可以认为变更的结果是无误的。

但是hash只能对一些基本数据类型使用,对于map等类型来讲,其实并不太准确,那么如何进行map类型checksum呢?

一、描述

对于json转map之后,通过转换前的json和转换后的map做checksum是不可行的,因为两者结果差别还是挺大的

select hash(map("hi","bili","hello","world"))
union all
select hash('{"hi":"bili","hello":"world"}')

结果:

map:  55979077
json: 1202933987

二、思路

那么对于json转map后的checksum应该怎么做?或者如何确定转换的结果没有错?

采用的方法是通过get_json_object和element_at方法来确定转换前后结果是否又误。

  1. 获取转换后map的所有key数值
  2. 将获取的key数值结果分别分配给每行数据
  3. 分别提取每个key的数据,比较是否相同

三、实现:

实现方法1:

将转换后所有的map结果的key提取出来

key_s as (
select
    -- map提取使用的key
    key_one,
    -- json提取使用的key
    concat('$.',key_one) object_one,
    1 as joinkey
  from (
    select
    	-- 将所有key去重合并在一起
      collect_set(key_one) keys
    from (
      select 
        -- 将每一行的key炸开
        key_one
      from (
        select 
        	-- 得到每一行数据的key
          map_keys(extended_map) keys
        from 
          tmp_bdp.test_map
      ) t1
      lateral view explode(keys) tmp as key_one
    ) t2
  ) t3
  lateral view explode(keys) tmp as key_one
)

然后和转换前后的json和map进行关联提取每个key数据

check_object as (
	select
    extended_fields,
    extended_map,
    1 as joinkey
  from 
    tmp_bdp.test_map
)

关联求解

check_key as (
  select
    t2.extended_fields,
    t2.extended_map,
    t1.key_one,
    t1.object_one
  from 
    key_s as t1
  join 
    check_object t2
  on t1.joinkey = t2.joinkey
)

select
  -- 求每一行的hash值,如果不相同,说明此行变换的有问题
  sum(hash(get_json_object(extended_fields, object_one))) json_hash,
  sum(hash(extended_map[key_one]))
from 
  check_key
实现方法2

上面上个方法虽然可以得解,但是有一些很大的资源消耗,因为是找到的所有的map的key,但是对于单行数据来说,其实有些key是用不到的,所以造成了很大的资源消耗,所以可以针对每一行进行提取key即可,然后在该行使用即可

with key_s as (
  select 
    extended_fields,  -- json
    extended_map,  -- map
    map_keys(extended_map) keys
  from 
    tmp_bdp.test_map
)

,check_key as (
  select
    extended_fields,
    extended_map,
  	-- 将key炸开,变为单个key
    key_one,
    concat('$.', key_one) key_json
  from 
    key_s
  lateral view explode(keys) tmp as key_one
)

  -- 求解每行的key即可
  select
    extended_fields,
    extended_map,
    hash(get_json_object(extended_fields, key_json)) json_hash,
    hash(extended_map[key_one] )map_hash
  from 
    check_key

TIPS:

其实这里有一个小问题,在测试的时候发现有些行是的hash值并不相同,检测之后发现是有些key是多个字段是组合的,比如:“bundle.ver.demo”,这样在map中可以获得value值,而在json中使用get_json_object提取时就会返回null值,所以需要把get_json_object方法改为使用json_tuple方法。

但是需要注意的是,get_json_object是一个UDF,而json_tuple是一个UDTF,所以在使用的时候返回的是多个值,需要炸开才可以,否则会报错:

FAILED: SemanticException [Error 10081]: UDTF's are not supported outside the SELECT clause, nor nested in expressions
改进

修改之后的代码就是:

select
  hash(json_s) json_hash,
  hash(extended_map[key_one])map_hash
from 
  check_key
-- 将json炸开
lateral view JSON_TUPLE(extended_fields, key_one) tmp as json_s

整体代码就变为了:

with key_s as (select 
    extended_fields,  -- json
    extended_map,  -- map
    map_keys(extended_map) keys
  from 
    tmp_bdp.test_map
)

,check_key as (
  select
    extended_fields,
    extended_map,
    key_one
  from 
    key_s
  lateral view explode(keys) tmp as key_one
)

,join_key as (
  select
    hash(json_s) json_hash,
    hash(extended_map[key_one])map_hash
  from 
    check_key
  lateral view JSON_TUPLE(extended_fields, key_one) tmp as json_s
)

select * from join_key

如果存在hash值不一样的情况,就说明变换前后是有问题的