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