Функция Sum Distinct by key (или Symmetric Aggregates) в BigQuery - PullRequest
0 голосов
/ 20 ноября 2019

Я пытаюсь суммировать раздуваемые / дублированные значения путем устранения дублирования на их ключе. Looker называет это симметричными агрегатами. Я хотел бы использовать постоянный UDF и не опираться на подзапросы. Looker имеет довольно элегантное решение, которое выглядит так, как будто его можно запекать в UDF.

Я пробовал:

CREATE OR REPLACE FUNCTION `fn.sumdistinct`(unique_key ANY TYPE, val_to_sum ANY TYPE) AS (
 COALESCE(ROUND(COALESCE(CAST((SUM(DISTINCT (CAST(ROUND(COALESCE(safe_cast(val_to_sum as float64) ,0)*(1/1000*1.0), 9) AS NUMERIC) + (cast(cast(concat('0x', substr(to_hex(md5(CAST(unique_key  AS STRING))), 1, 15)) as int64) as numeric) * 4294967296 + cast(cast(concat('0x', substr(to_hex(md5(CAST(unique_key  AS STRING))), 16, 8)) as int64) as numeric)) * 0.000000001 )) - SUM(DISTINCT (cast(cast(concat('0x', substr(to_hex(md5(CAST(unique_key  AS STRING))), 1, 15)) as int64) as numeric) * 4294967296 + cast(cast(concat('0x', substr(to_hex(md5(CAST(unique_key  AS STRING))), 16, 8)) as int64) as numeric)) * 0.000000001) )  / (1/1000*1.0) AS FLOAT64), 0), 6), 0)
);

, но получаю:

Invalid function fn.sumdistinct. Aggregate function SUM not allowed in templated SQL function call

Я ищу функцию, которая может преобразовать это:

id   val
1    100
2    200
2    200
3    300
3    300
3    300

в:

unique_ids  total_value
3           600

Ответы [ 4 ]

1 голос
/ 22 ноября 2019

Ниже для BigQuery Standard SQL

#standardSQL
CREATE TEMP FUNCTION SumDistinct(arr ANY TYPE) AS ((
  SELECT AS STRUCT 
    COUNT(DISTINCT id) unique_ids, 
    SUM(val) total_value
  FROM (
    SELECT ANY_VALUE(t).*
    FROM UNNEST(arr) t
    GROUP BY FORMAT('%t', t)
  )
));
SELECT SumDistinct(ARRAY_AGG(STRUCT(id, val))).*
FROM `project.dataset.data`   

Если применить к образцу данных из вашего вопроса - результат

Row unique_ids  total_value  
1   3           600 
1 голос
/ 22 ноября 2019

Вы определенно можете делать это без внешнего GROUP BY:

CREATE OR REPLACE FUNCTION `dataset.sumdistinct` (values array<struct<id int64, val int64>>) as (
  (
    select 
      struct(
       count(distinct x.id) as col1, 
       sum(distinct x.val) as col2
      ) from unnest(values) as x
  )
);

select sumdistinct(array_agg(struct(id as id, val as val))) from `dataset.table`
0 голосов
/ 21 ноября 2019

Судя по комментариям, вы хотите, чтобы UDF мог вызывать функцию агрегирования внутри. Возможно, вы ищете пользовательскую статистическую функцию, которая не поддерживается в BigQuery, но может быть выполнена в следующей форме:

Вывод не соответствует ожидаемому, поскольку UDF не может вывести 2 столбца, как в вашем примере, надеюсь, у вас есть идея, что вам нужно array_agg () поле и ваш UDF выполняет UNNEST () внутри и сможет использовать агрегирующие функции системы, такие как SUM ():

CREATE TEMP FUNCTION sumdistinct (unique_key INT64, val_array ARRAY<INT64>) AS (
 (SELECT COALESCE(ROUND(COALESCE(CAST((SUM(DISTINCT (CAST(ROUND(COALESCE(safe_cast(val_to_sum as float64) ,0)*(1/1000*1.0), 9) AS NUMERIC) + (cast(cast(concat('0x', substr(to_hex(md5(CAST(unique_key  AS STRING))), 1, 15)) as int64) as numeric) * 4294967296 + cast(cast(concat('0x', substr(to_hex(md5(CAST(unique_key  AS STRING))), 16, 8)) as int64) as numeric)) * 0.000000001 )) - SUM(DISTINCT (cast(cast(concat('0x', substr(to_hex(md5(CAST(unique_key  AS STRING))), 1, 15)) as int64) as numeric) * 4294967296 + cast(cast(concat('0x', substr(to_hex(md5(CAST(unique_key  AS STRING))), 16, 8)) as int64) as numeric)) * 0.000000001) )  / (1/1000*1.0) AS FLOAT64), 0), 6), 0)
 FROM unnest(val_array) val_to_sum)
);
with data as 
(select 1 as id, 100 as val union all
select 1, 100 union all
select 1, 100 union all
select 2, 200 union all
select 2, 200 union all
select 3, 300 union all
select 3, 300
)
SELECT sumdistinct(id, array_agg(val))
FROM data
GROUP BY id
0 голосов
/ 20 ноября 2019

В зависимости от того, как вы хотите разрешить разные значения val для одного и того же идентификатора, вы можете настроить функцию агрегирования (max(val)) ниже sql:

with data as 
(select 1 as id, 100 as val union all
select 1, 100 union all
select 1, 100 union all
select 2, 200 union all
select 2, 200 union all
select 3, 300 union all
select 3, 300
)
SELECT count(1) as unique_ids, sum(val) as total_value
FROM (
SELECT id, max(val) val
FROM data
GROUP BY id
)
...