如何有效地将两列范围转换为扩展表?

问题描述 投票:1回答:1

我正在尝试在雪花中使用地理IP数据。这涉及几件事:

1)具有CIDR IP范围和geoname_ID及其纬度/经度坐标的源表

2)我使用了parse_ip函数,并将range_start和range_end值提取为ipv4 0-4.2bn范围内的简单整数列。一些范围包含1个IP,某些范围可能多达1670万。

因此,中间表数据中的310万行看起来像这样:

RANGE_START RANGE_END GEONAME_ID LATITUDE LONGITUDE

214690946 214690946 4556793 39.84980011 -75.37470245

214690947 214690947 6252001 37.75099945 -97.82199860

214690948 214690951 6252001 37.75099945 -97.82199860

214690952 214690959 6252001 37.75099945 -97.82199860

214690960 214690975 6252001 37.75099945 -97.82199860

如您所见,一个地理名称ID可以有多个范围与其关联。

问题在于,将一个(解析为整数值)IP与该表连接需要非等式连接,这在雪花中目前非常缓慢(根据经验,速度要慢1000倍)。因此,我想将上面的表格扩展为每个IP范围有一行,即,最后一行的范围为214690960至214690975,将变成16行,同时为每个新行保留geoname和lat long。我唯一想到的方法是对生成器表进行非等价连接,但这在3xl上花了30分钟,包含1000行,生成了约120万行结果。我在这个范围内有310万行要展平,所以行不通。

任何想法,有人吗?这是我到目前为止尝试过的:

create OR REPLACE table GENERATOR_TABLE (IP INT);

INSERT INTO GENERATOR_TABLE  SELECT ROW_NUMBER() over (ORDER BY NULL)  AS IP FROM TABLE(GENERATOR(ROWCOUNT => 4228250627)) ORDER BY IP;

create or replace table GEO_INTERMEDIARY as 
(select network_parsed:ipv4_range_start::number as range_start, network_parsed:"ipv4_range_end"::number range_end, geoname_id, latitude, longitude from  GEO_SOURCE order by range_start, range_end);

CREATE OR REPLACE TABLE EXPANDED_GEO AS 
select * from (select * from GEO_INTERMEDIARY order by geoname_id limit 1000 offset 0) A
JOIN GENERATOR_TABLE B ON B.IP >= A.RANGE_START AND B.IP <= A.RANGE_END
ORDER BY IP;
join snowflake-data-warehouse
1个回答
3
投票

对于这种模式,您确实可以尝试使用生成器,但是我通常最终使用JavaScript UDTFs

这是示例功能和数据用法:

create or replace table x(
RANGE_START int,
RANGE_END int,
GEONAME_ID int,
LATITUDE double,
LONGITUDE double
) as 
select * from values
(214690946,214690946,4556793,39.84980011,-75.37470245),
(214690947,214690947,6252001,37.75099945,-97.82199860),
(214690948,214690951,6252001,37.75099945,-97.82199860);

create or replace function magic(
  range_start double,
  range_end double,
  geoname_id double,
  latitude double,
  longitude double
) 
returns table (
  ip double,
  geoname_id double,
  latitude double,
  longitude double
) language javascript as 
$$
{
  processRow: function(row, rowWriter, context) {
    let start = row.RANGE_START
    let end = row.RANGE_END

    while (start <= end) {
      rowWriter.writeRow({
        IP: start,
        GEONAME_ID: row.GEONAME_ID,
        LATITUDE: row.LATITUDE,
        LONGITUDE: row.LONGITUDE,
      });
      start++;
    }
  }
}
$$;
select m.* from x, 
  table(magic(range_start::double, range_end::double, 
              geoname_id::double, latitude, longitude)) m;
-----------+------------+-------------+--------------+
    IP     | GEONAME_ID |  LATITUDE   |  LONGITUDE   |
-----------+------------+-------------+--------------+
 214690946 | 4556793    | 39.84980011 | -75.37470245 |
 214690947 | 6252001    | 37.75099945 | -97.8219986  |
 214690948 | 6252001    | 37.75099945 | -97.8219986  |
 214690949 | 6252001    | 37.75099945 | -97.8219986  |
 214690950 | 6252001    | 37.75099945 | -97.8219986  |
 214690951 | 6252001    | 37.75099945 | -97.8219986  |
-----------+------------+-------------+--------------+

这里唯一的陷阱是,JS仅支持double类型,但是对于此数据,可以的,您不会看到任何精度损失。

我在产生10M IP的1M范围内对其进行了测试,它在几秒钟内完成。

© www.soinside.com 2019 - 2024. All rights reserved.