如何应用:大查询中的count(distinct ...)超过(partition by ... order by)?

Posted

技术标签:

【中文标题】如何应用:大查询中的count(distinct ...)超过(partition by ... order by)?【英文标题】:How to apply: count(distinct ...) over (partition by ... order by) in big query? 【发布时间】:2021-12-18 01:42:37 【问题描述】:

我目前有这个source table。

我正在尝试在 GCP BigQuery 上的 SQL 中从第一个表中获取 this second table。

我的查询如下:

        SELECT
            SE.MARKET_ID,
            SE.LOCAL_POS_ID,
            SE.BC_ID,
            LEFT(SE.SALE_CREATION_DATE,6) AS DATE_ID_MONTH,

            COUNT(DISTINCT
                CASE
                    WHEN FLAG
                    THEN SE.CUST_ID
                END)
            OVER (PARTITION BY SE.MARKET_ID, SE.LOCAL_POS_ID, SE.BC_ID, LEFT(SE.SALE_CREATION_DATE,4) ORDER BY LEFT(SE.SALE_CREATION_DATE,6)) AS NB_ACTIVE_CUSTOMERS

        FROM
            SE
        GROUP BY
            SE.MARKET_ID, SE.LOCAL_POS_ID, SE.BC_ID, LEFT(SE.SALE_CREATION_DATE,6)

但是,我收到了这个我没有成功绕过的错误:

Window ORDER BY is not allowed if DISTINCT is specified at [12:107]

我无法使用以下请求创建以前的表:

SELECT DISTINCT
        SE.MARKET_ID,
        SE.LOCAL_POS_ID,
        SE.BC_ID,
        LEFT(SE.SALE_CREATION_DATE,6) AS DATE_ID_MONTH,
        CASE
            WHEN FLAG
            THEN SE.CUST_ID
            ELSE NULL
        END AS VALID_CUST_ID
FROM
        SE

为了在那之后使用dense_rank(),因为我有50个其他指标(和500M行)要添加到这个表(基于其他标志的指标),我显然不能为每个指标创建一个WITH,我只需要在几个 WITH 或 none 中使用它(就像我当前的查询应该做的那样)。

请问有人知道我该如何处理吗?

【问题讨论】:

是的,COUNT(DISTINCT <expr>) 不是窗口函数; COUNT(<expr>) 错误很明显 - Window ORDER BY is not allowed if DISTINCT is specified!因此,您应该通过输入数据、预期输出和清晰逻辑的示例来围绕您想要实现的目标提出问题 请看我在主题中的超链接:) 【参考方案1】:

考虑以下方法

select * except(ids), 
  array_length(array(
    select distinct id
    from unnest(split(ids)) id
  )) as nb_active_customers, 
  format('%t', array(
    select distinct id
    from unnest(split(ids)) id
  )) as distinct_values
from (
  select market_id, local_pos_id, bc_id, date_id_month,
    string_agg('' || ids) over(partition by market_id order by date_id_month) ids
  from (
    select market_id, local_pos_id, bc_id, left(sale_creation_date,6) AS date_id_month,
      string_agg('' || cust_id) ids
    from se
    where flag = 1
    group by market_id, local_pos_id, bc_id, date_id_month
  )
) t          

如果应用于您问题中的样本数据 - 输出是

【讨论】:

【参考方案2】:

我认为您的某些示例数据不正确,但我确实使用它并获得了匹配的结果,至少对于 MPE 数据。您可以通过首先在CUST_ID 上使用额外分区标记“明显计数”行,然后在FLAG DESC 上首先排序来完成此操作。然后你会以你希望申请count(distinct <expr>) over ...的相同方式总结它

WITH SE AS (
    SELECT  1 LINE_ID, 'TW' MARKET_ID, 'X' LOCAL_POS_ID, 'MPE' BC_ID,
            1 CUST_ID, '20200201' SALE_CREATION_DATE, 1 FLAG UNION ALL
    SELECT  2, 'TW', 'X', 'MPE', 2, '20201005', 1 UNION ALL
    SELECT  3, 'TW', 'X', 'MPE', 3, '20200415', 0 UNION ALL
    SELECT  4, 'TW', 'X', 'MPE', 1, '20200223', 1 UNION ALL
    SELECT  5, 'TW', 'X', 'MPE', 6, '20200217', 1 UNION ALL
    SELECT  6, 'TW', 'X', 'MPE', 9, '20200715', 1 UNION ALL
    SELECT  7, 'TW', 'X', 'MPE', 4, '20200223', 1 UNION ALL
    SELECT  8, 'TW', 'X', 'MPE', 1, '20201008', 1 UNION ALL
    SELECT  9, 'TW', 'X', 'MPE', 2, '20201019', 1 UNION ALL
    SELECT 10, 'TW', 'X', 'MPE', 1, '20200516', 1 UNION ALL
    SELECT 11, 'TW', 'X', 'MPE', 1, '20200129', 1 UNION ALL
    SELECT 12, 'TW', 'X', 'MPE', 1, '20201007', 1 UNION ALL
    SELECT 13, 'TW', 'X', 'MPE', 2, '20201005', 1 UNION ALL
    SELECT 14, 'TW', 'X', 'MPE', 3, '20200505', 1 UNION ALL
    SELECT 15, 'TW', 'X', 'MPE', 8, '20201103', 1 UNION ALL
    SELECT 16, 'TW', 'X', 'MPE', 9, '20200820', 1
),
DATA AS (
    SELECT *,
        LEFT(SALE_CREATION_DATE, 6) AS SALE_MONTH,
        LEFT(SALE_CREATION_DATE, 4) AS SALE_YEAR,
        CASE ROW_NUMBER() OVER (
            PARTITION BY MARKET_ID, LOCAL_POS_ID, BC_ID,
                         LEFT(SALE_CREATION_DATE, 4), CUST_ID
            ORDER BY FLAG DESC, LEFT(SALE_CREATION_DATE, 6)
        ) WHEN 1 THEN FLAG END AS COUNTER /* assumes possible to have no flagged row */
    FROM SE
)
SELECT MARKET_ID, LOCAL_POS_ID, BC_ID, SALE_MONTH,
    SUM(SUM(COUNTER)) OVER (
            PARTITION BY MARKET_ID, LOCAL_POS_ID, BC_ID, SALE_YEAR
            ORDER BY SALE_MONTH
    ) AS NB_ACTIVE_CUSTOMERS
FROM DATA
GROUP BY MARKET_ID, LOCAL_POS_ID, BC_ID, SALE_YEAR, SALE_MONTH
ORDER BY MARKET_ID, LOCAL_POS_ID, BC_ID, SALE_YEAR, SALE_MONTH

【讨论】:

这个解决方案是正确的,非常感谢!对于阅读本文的人来说,Mikhail Berlyant 在本主题中的答案也适用,但适用于单个 KPI 计算。在我的情况下,我有其他 KPI 可以在同一个查询中计算(使用来自同一个源表的其他标志条件),但是“where flag = 1”使它成为不可能,因为它减少了源数据的行数。 shawnt00 中 sum(sum(...)) over (...) 的解决方案允许我为每个 KPI 添加一个新的“CASE ROW_NUMBER() ...”以在源上计算。问候

以上是关于如何应用:大查询中的count(distinct ...)超过(partition by ... order by)?的主要内容,如果未能解决你的问题,请参考以下文章

选择 Count (distinct col) 查询以显示结果中的行数和列数 - postgresql

使用子查询可提升 COUNT DISTINCT 速度 50 倍

mongo中的高级查询之聚合操作(distinct,count,group)与数据去重

大数据之-HIVE入门(十二)

在插入 table2 之前,如何在 table1 的多个列上应用 count 和 distinct

Eloquent count distinct 返回错误的总数