【问题标题】:How can I efficiently transform a two-column range into an expanded table?如何有效地将两列范围转换为扩展表?
【发布时间】:2019-10-16 16:41:48
【问题描述】:

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

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

2) 我使用了 parse_ip 函数并将 range_start 和 range_end 值提取为 ipv4 0-42 亿范围内的简单整数列。有些范围包含 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。我能想到的唯一方法是对生成器表进行非 equi 连接,但这在 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-cloud-data-platform


    【解决方案1】:

    对于这种模式,你确实可以尝试使用生成器,但我通常最终使用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类型,但是对于这个数据,没关系,你不会看到任何精度损失。

    我在产生 1000 万个 IP 的 100 万个范围上对其进行了测试,它在几秒钟内完成。

    【讨论】:

    • 谢谢你,马辛。试图避免使用 UDF,因为在我的公司创建 UDF 很痛苦,因为我们的 CO 控制缓慢,但性能改进足以证明该过程的合理性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 2017-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-14
    相关资源
    最近更新 更多