在窗口函数内过滤后如何排名?

问题描述 投票:0回答:1

我有一个巨大的 BigQuery 表“taskperformance”,它将任务归因于销售(在本例中进行排序是为了更容易 理解):

销售_id 销售_时间戳 任务id 任务_时间戳 是同一个商店 is_retail_task
S123 2024-01-06 10:00:00 世界标准时间 T1 2024-01-05 09:00:00 世界标准时间 正确 正确
S123 2024-01-06 10:00:00 世界标准时间 T2 2024-01-04 10:00:00 世界标准时间 错误 错误
S123 2024-01-06 10:00:00 世界标准时间 T3 2024-01-03 14:00:00 世界标准时间 正确 错误
S123 2024-01-06 10:00:00 世界标准时间 T4 2024-01-02 16:00:00 世界标准时间 正确 正确
S456 2024-02-15 09:30:00 世界标准时间 T5 2024-02-14 15:00:00 世界标准时间 正确 错误
S456 2024-02-15 09:30:00 世界标准时间 T6 2024-02-13 12:00:00 世界标准时间 错误 正确
S456 2024-02-15 09:30:00 世界标准时间 T7 2024-02-12 11:00:00 世界标准时间 正确 正确

我想在此表中添加两列:

  • same_store_rank:对于每次销售,我对 is_same_store = TRUE 的任务进行排名,按 task_timestamp 排序 下降。当 is_same_store = FALSE 时,排名为 NULL。
  • retail_task_rank:对于每次销售,我对 is_retail_task = TRUE 的任务进行排名,按 task_timestamp 排序 下降。当 is_retail_task = FALSE 时,排名为 NULL。

所以我想获得这个:

销售_id 销售_时间戳 任务id 任务_时间戳 是同一个商店 is_retail_task 相同商店排名 零售_任务_排名
S123 2024-01-06 10:00:00 世界标准时间 T1 2024-01-05 09:00:00 世界标准时间 真实 真实 1 1
S123 2024-01-06 10:00:00 世界标准时间 T2 2024-01-04 10:00:00 世界标准时间
S123 2024-01-06 10:00:00 世界标准时间 T3 2024-01-03 14:00:00 世界标准时间 真实 2
S123 2024-01-06 10:00:00 世界标准时间 T4 2024-01-02 16:00:00 世界标准时间 真实 真实 3 2
S456 2024-02-15 09:30:00 世界标准时间 T5 2024-02-14 15:00:00 世界标准时间 真实 1
S456 2024-02-15 09:30:00 世界标准时间 T6 2024-02-13 12:00:00 世界标准时间 真实 1
S456 2024-02-15 09:30:00 世界标准时间 T7 2024-02-12 11:00:00 世界标准时间 真实 真实 2 2

但在应用 RANK() 之前,我似乎找不到一种方法来过滤窗口函数内的条件。

当我尝试时,条件为假的行的排名仍然会增加:

WITH taskperformance AS (
SELECT 'S123' AS sale_id, TIMESTAMP('2024-01-06 10:00:00 UTC') AS sale_timestamp, 'T1' AS task_id, TIMESTAMP('2024-01-05 09:00:00 UTC') AS task_timestamp, TRUE AS is_same_store, TRUE AS is_retail_task
UNION ALL
SELECT 'S123', TIMESTAMP('2024-01-06 10:00:00 UTC'), 'T2', TIMESTAMP('2024-01-04 10:00:00 UTC'), FALSE, FALSE
UNION ALL
SELECT 'S123', TIMESTAMP('2024-01-06 10:00:00 UTC'), 'T3', TIMESTAMP('2024-01-03 14:00:00 UTC'), TRUE, FALSE
UNION ALL
SELECT 'S123', TIMESTAMP('2024-01-06 10:00:00 UTC'), 'T4', TIMESTAMP('2024-01-02 16:00:00 UTC'), TRUE, TRUE
UNION ALL
SELECT 'S456', TIMESTAMP('2024-02-15 09:30:00 UTC'), 'T5', TIMESTAMP('2024-02-14 15:00:00 UTC'), TRUE, FALSE
UNION ALL
SELECT 'S456', TIMESTAMP('2024-02-15 09:30:00 UTC'), 'T6', TIMESTAMP('2024-02-13 12:00:00 UTC'), FALSE, TRUE
UNION ALL
SELECT 'S456', TIMESTAMP('2024-02-15 09:30:00 UTC'), 'T7', TIMESTAMP('2024-02-12 11:00:00 UTC'), TRUE, TRUE
)
SELECT
    *,
    CASE WHEN is_same_store THEN
    RANK() OVER (PARTITION BY sale_id ORDER BY task_timestamp DESC)
    END AS same_store_rank,
    CASE WHEN is_retail_task THEN
    RANK() OVER (PARTITION BY sale_id ORDER BY task_timestamp DESC)
    END AS retail_task_rank
FROM taskperformance
ORDER BY task_id
750a
销售_id 销售_时间戳 任务ID 任务_时间戳 是同一个商店 is_retail_task 相同商店排名 零售_任务_排名
S123 2024-01-06 10:00:00 世界标准时间 T1 2024-01-05 09:00:00 世界标准时间 真实 真实 1 1
S123 2024-01-06 10:00:00 世界标准时间 T2 2024-01-04 10:00:00 世界标准时间
S123 2024-01-06 10:00:00 世界标准时间 T3 2024-01-03 14:00:00 世界标准时间 真实 3
S123 2024-01-06 10:00:00 世界标准时间 T42024-01-02 16:00:00 世界标准时间 真实 真实 4 4
S456 2024-02-15 09:30:00 世界标准时间 T5 2024-02-14 15:00:00 世界标准时间 真实 1
S456 2024-02-15 09:30:00 世界标准时间 T6 2024-02-13 12:00:00 世界标准时间 真实 2
S456 2024-02-15 09:30:00 世界标准时间 T7 2024-02-12 11:00:00 世界标准时间 真实 真实 3 3

例如,对于 same_store_rank,我得到 [1, NULL, 3, 4] 而不是 [1, NULL, 2, 3]

我认为这可以使用 CTE 和 WHERE 子句来完成,然后加入任务性能表,但这似乎 浪费了读这个表 3 遍的时间:

WITH
    taskperformance AS (
        SELECT 'S123' AS sale_id, TIMESTAMP('2024-01-06 10:00:00 UTC') AS sale_timestamp, 'T1' AS task_id, TIMESTAMP('2024-01-05 09:00:00 UTC') AS task_timestamp, TRUE AS is_same_store, TRUE AS is_retail_task
        UNION ALL
        SELECT 'S123', TIMESTAMP('2024-01-06 10:00:00 UTC'), 'T2', TIMESTAMP('2024-01-04 10:00:00 UTC'), FALSE, FALSE
        UNION ALL
        SELECT 'S123', TIMESTAMP('2024-01-06 10:00:00 UTC'), 'T3', TIMESTAMP('2024-01-03 14:00:00 UTC'), TRUE, FALSE
        UNION ALL
        SELECT 'S123', TIMESTAMP('2024-01-06 10:00:00 UTC'), 'T4', TIMESTAMP('2024-01-02 16:00:00 UTC'), TRUE, TRUE
        UNION ALL
        SELECT 'S456', TIMESTAMP('2024-02-15 09:30:00 UTC'), 'T5', TIMESTAMP('2024-02-14 15:00:00 UTC'), TRUE, FALSE
        UNION ALL
        SELECT 'S456', TIMESTAMP('2024-02-15 09:30:00 UTC'), 'T6', TIMESTAMP('2024-02-13 12:00:00 UTC'), FALSE, TRUE
        UNION ALL
        SELECT 'S456', TIMESTAMP('2024-02-15 09:30:00 UTC'), 'T7', TIMESTAMP('2024-02-12 11:00:00 UTC'), TRUE, TRUE
    ),
    same_store_ranks AS (
        SELECT sale_id, task_id, RANK() OVER (PARTITION BY sale_id ORDER BY task_timestamp DESC) AS same_store_rank
        FROM taskperformance
        WHERE is_same_store
    ),
    retail_task_ranks AS (
        SELECT sale_id, task_id, RANK() OVER (PARTITION BY sale_id ORDER BY task_timestamp DESC) AS retail_task_rank
        FROM taskperformance
        WHERE is_retail_task
    )

SELECT *
FROM taskperformance
LEFT JOIN same_store_ranks
USING(sale_id, task_id)
LEFT JOIN retail_task_ranks
USING(sale_id, task_id)
ORDER BY task_id

这样做,我得到了预期的结果,但我想避免多次读取任务性能,然后不得不 JOIN,因为代码很冗长,而且我认为多次读取巨大的任务性能表对 表现。特别是因为我还有其他排名列要添加,而不仅仅是本例中的两个。

sql google-bigquery window-functions ranking-functions
1个回答
0
投票

您可以按相关列进行分区以获得连续排名

SELECT
    *,
    CASE WHEN is_same_store THEN
    RANK() OVER (PARTITION BY sale_id, is_same_store ORDER BY task_timestamp DESC)
    END AS same_store_rank,
    CASE WHEN is_retail_task THEN
    RANK() OVER (PARTITION BY sale_id, is_retail_task ORDER BY task_timestamp DESC)
    END AS retail_task_rank
FROM taskperformance
ORDER BY task_id
© www.soinside.com 2019 - 2024. All rights reserved.