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

How can I efficiently transform a two-column range into an expanded table?

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

1) 具有 CIDR IP 范围和 geoname_ID 及其 lat/long 坐标

的来源 table

2) 我使用了 parse_ip 函数并将 range_start 和 range_end 值提取为 ipv4 0-42 亿范围内的简单整数列。有些范围由 1 个 IP 组成,有些可能多达 1670 万。

因此,中间 table 数据中的 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 可以关联多个范围。

问题是用这个 table 连接一个(解析为整数值)IP 需要非相等连接,这在雪花中非常慢(根据经验大约慢 1000 倍)。所以我想将上面的 table 扩展为范围内的每个 IP 一行,即范围为 214690960 到 214690975 的最后一行将变成 16 行,同时为每个新行保留 geoname 和 lat long .我能想到的唯一方法是对生成器 table 进行非等值连接,但这在 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;

对于这种模式,您确实可以尝试使用生成器,但我通常最终会使用 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 范围内对其进行了测试,它在几秒钟内完成。