Как эффективно преобразовать диапазон из двух столбцов в расширенную таблицу? - PullRequest
0 голосов
/ 16 октября 2019

Я пытаюсь использовать данные гео IP в снежинке. Это включает в себя несколько вещей:

1) Исходная таблица с диапазоном CIDR IP и geoname_ID и его координатами широты / долготы

2) Я использовал функцию parse_ip и извлек диапазон_старт иЗначения range_end - простые целочисленные столбцы в диапазоне ipv4 0-4.2bnНекоторые диапазоны состоят из 1 IP, некоторые могут иметь целых 16,7 млн.

Итак, 3,1 млн. Строк в данных промежуточной таблицы выглядят примерно так:

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 строк, сохраняя при этом геонаме и долготу лота для каждой новой строки. Единственный способ, которым я мог подумать, это сделать не-равное соединение с таблицей генератора, но это заняло 30 минут на 3xl для 1000 строк, генерируя около 1,2 миллиона строк результата. У меня есть 3,1 миллиона строк в этом диапазоне, чтобы сгладить, так что это не сработает.

Есть идеи, кто-нибудь? Вот что я попробовал до сих пор:

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;

1 Ответ

3 голосов
/ 18 октября 2019

Для такого шаблона вы действительно можете попробовать использовать генератор, но я обычно заканчиваю тем, что 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, но для этих данных все в порядке, вы не увидите потери точности.

Я тестировал его на диапазонах 1M, производя 10M IP, этозакончил в считанные секунды.

...