【问题标题】:Effective way to join tables by range using impala使用 impala 按范围连接表的有效方法
【发布时间】:2017-02-22 06:41:18
【问题描述】:

我有以下表格,第一个 (Range) 包括值范围和附加列:

row  | From   |  To     | Country ....
-----|--------|---------|---------
1    | 1200   |   1500  |
2    | 2200   |   2700  |
3    | 1700   |   1900  |
4    | 2100   |   2150  |
... 

FromTobigint 并且是专有的。 Range 表包含 180 万条记录。 附加表 (Values) 包含 270 万条记录,如下所示:

 row     | Value  | More columns....
 --------|--------|----------------
    1    | 1777   |    
    2    | 2122   |    
    3    | 1832   |    
    4    | 1340   |    
    ... 

我想创建一个如下表:

row      | Value  | From   | To    | More columns....
 --------|--------|--------|-------|---
    1    | 1777   | 1700   | 1900  |
    2    | 2122   | 2100   | 2150  |   
    3    | 1832   | 1700   | 1900  |   
    4    | 1340   | 1200   | 1500  |   
    ... 

我使用BETWEEN 完成上述任务,但查询永远不会结束:

VALUES.VALUE between RANGE.FROM and RANGE.TO

我需要在表分区或 Impala 中进行更改吗?

【问题讨论】:

  • 它是“使用尽可能多的关键字来定义表”挑战的一部分吗?

标签: sql hive between impala


【解决方案1】:

以下解决方案的主要思想是将θ连接(非等连接)替换为等连接,这将导致良好的分布+高效的本地连接算法。

范围 (-infinity,infinity) 被拆分为 n 长度的部分。
范围表中的每个范围都与其相交的部分相关联。

例如给定 n=1000,范围 [1652,3701] 将与部分 [1000,2000)[2000,3000)[3000,4000) 相关联(并且将有 3 条记录,每个部分 1 条)

               1652              3701
               |                 |
               -------------------

-------------------------------------------------------
|        |        |        |        |        |                
0        1000     2000     3000     4000     5000 

以同样的方式,值表中的值与包含它的范围相关联,例如2093 将与[2000,3000) 范围相关联。

两个表之间的连接将在代表该部分的值上,例如[1652,3701]2093 将加入 [2000,3000) 部分


create table val_range (id int,from_val bigint,to_val bigint);

insert into val_range values
    (1,1200,1500)
   ,(2,2200,2700)
   ,(3,1700,1900)
   ,(4,2100,2150)
;   

create table val (id int,val bigint);

insert into val values
    (1,1777)    
   ,(2,2122)    
   ,(3,1832)    
   ,(4,1340)
;   

set n=1000;

select      v.id
           ,v.val
           ,r.from_val
           ,r.to_val

from       (select  r.*
                   ,floor(from_val/${hiveconf:n}) + pe.i    as match_val

            from    val_range r
                    lateral view    posexplode
                                    (
                                        split
                                        (
                                            space
                                            (
                                                cast
                                                (
                                                    floor(to_val/${hiveconf:n}) 
                                                  - floor(from_val/${hiveconf:n}) 

                                                    as int
                                                )
                                            )
                                           ,' '
                                        )
                                    ) pe as i,x
            ) r

            join    val v

            on      floor(v.val/${hiveconf:n})    =
                    r.match_val

where       v.val between r.from_val and r.to_val

order by    v.id        
;

+------+-------+------------+----------+
| v.id | v.val | r.from_val | r.to_val |
+------+-------+------------+----------+
|    1 |  1777 |       1700 |     1900 |
|    2 |  2122 |       2100 |     2150 |
|    3 |  1832 |       1700 |     1900 |
|    4 |  1340 |       1200 |     1500 |
+------+-------+------------+----------+

【讨论】:

  • 谢谢嘟嘟!您能否为您的出色解决方案添加一些解释?
  • 我会的,但你应该知道这是 Hive 的解决方案
  • 谢谢,我看到了。我可以使用 Hive 或 Impala。 Impala 更可取,但如果它仅在 Hive 中有效,我将在 Hive 中使用它。
  • 嗨,Avi,它对你有用吗?你能分享一些执行指标吗?
  • 谢谢嘟嘟!你是专家!有效!!!我会尝试看看我是否可以重新运行并获得指标。
【解决方案2】:

我在 IP 地址和 GeoIP 数据库方面遇到了类似的问题。范围连接非常慢,但通过“桶”连接,然后使用范围条件(where)进行精炼就可以了。

select
    g.country_iso_code,
    count(1) as cnt
from access_log as a
join geoip as g
  -- number of IPv4 addresses: 2**32 ==> 10 digits
  -- max network size: 2**23 ==> 8.3M ==> 7 digits 
  --   check with: select max(net_end-net_start) from geoip
  on round(a.int_ip, -7) = round(g.net_start, -7) -- <<== Step 1. bucket IP addresses
where a.date between "2021-12-01" and "2021-12-31"
  and a.int_ip between g.net_start and g.net_end  -- <<== Step 2. refine
group by g.country_iso_code
order by cnt desc

int_ip 是转换成整数的 IPv4 IP 地址。例如。 192.168.1.1 是 3232235777。net_startnet_end 表示类似。

如果允许某些不准确,那么您可以将舍入减少到 -6、-5、... 位数,这将加快执行速度,但大型网络可能会被错误分类。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-19
    相关资源
    最近更新 更多