雪花:不支持窗口函数“范围”,如何查询?

时间:2021-01-15 15:58:47

标签: snowflake-cloud-data-platform

我有一个包含 txn_date 和 cust_id 的交易表。

对于在 12 月进行过交易的每个客户,我想知道该客户在给定交易之前的 90 天内进行了多少交易。

这似乎是我可以使用窗口函数和 RANGE 滑动窗口运行的查询,但 Snowflake 不支持 RANGE 滑动窗口框架。

如何在 Snowflake 中运行此查询?

2 个答案:

答案 0 :(得分:0)

这样的事情怎么样:

WITH T1 AS (
    SELECT CUSTOMER_ID, TX_DATE
    FROM TRANSACTIONS
    WHERE TX_DATE BETWEEN '2020-12-01' AND '2020-12-31')
SELECT T2.CUSTOMER_ID, T2.TX_DATE
FROM TRANSACTIONS T2
INNER JOIN T1 ON T2.CUSTOMER_ID = T2.CUSTOMER_ID
WHERE T2.TX_DATE BETWEEN (T1.TX_DATE - 90) AND T1.TX_DATE

答案 1 :(得分:0)

一开始 NickW 的回答大致相同。

WITH data AS (
    SELECT txn_date::timestamp_ntz as txn_date, cust_id, txn_id
    FROM VALUES
        ('2020-12-04',0, 0),
        ('2020-12-03',1, 1),
        ('2020-11-04',1, 2),
        ('2020-10-04',1, 3),
        ('2020-09-04',1, 4), -- just on 90 days
        ('2020-09-02',1, 5), -- too far
        ('2021-01-05',1, 6)  -- in the future
        v(txn_date , cust_id, txn_id)
), dec_txn AS (
    SELECT txn_id,
        cust_id,
        DATEADD('day',-90, txn_date) AS win_start,
        txn_date AS win_end
    FROM data 
    WHERE date_trunc('month', txn_date) = '2020-12-01'
)
SELECT dt.*
    ,t.*
    ,datediff('days', dt.win_end, t.txn_date) as win_time
FROM dec_txn AS dt
LEFT JOIN data AS t 
    ON t.cust_id = dt.cust_id 
    AND t.txn_date between dt.win_start and win_end AND t.txn_id != dt.txn_id
    ;

给出:

TXN_ID   CUST_ID    WIN_START                 WIN_END                   TXN_DATE                  CUST_ID   TXN_ID   WIN_TIME
1        1          2020-09-04 00:00:00.000   2020-12-03 00:00:00.000   2020-11-04 00:00:00.000   1         2        -29
1        1          2020-09-04 00:00:00.000   2020-12-03 00:00:00.000   2020-10-04 00:00:00.000   1         3        -60
1        1          2020-09-04 00:00:00.000   2020-12-03 00:00:00.000   2020-09-04 00:00:00.000   1         4        -90
0        0          2020-09-05 00:00:00.000   2020-12-04 00:00:00.000   NULL                      NULL      NULL     NULL

因此,我们:

WITH data AS (
    SELECT txn_date::timestamp_ntz as txn_date, cust_id, txn_id
    FROM VALUES
        ('2020-12-04',0, 0),   
        ('2020-12-03',1, 1),   
        ('2020-11-04',1, 2),
        ('2020-10-04',1, 3),
        ('2020-09-04',1, 4), -- just on 90 days
        ('2020-09-02',1, 5), -- too far
        ('2021-01-05',1, 6) -- in the future
        v(txn_date , cust_id, txn_id)
), dec_txn AS (
    SELECT txn_id,
        cust_id,
        txn_date,
        DATEADD('day',-90, txn_date) AS win_start,
        txn_date AS win_end
    FROM data 
    WHERE date_trunc('month', txn_date) = '2020-12-01'
)
SELECT dt.cust_id
    ,dt.txn_id
    ,dt.txn_date
    ,count(t.txn_id) as c__prior_90_days_transaction
FROM dec_txn AS dt
LEFT JOIN data AS t 
ON t.cust_id = dt.cust_id 
AND t.txn_date >= dt.win_start and t.txn_date < dt.win_end AND t.txn_id != dt.txn_id
GROUP BY 1,2,3
ORDER BY 1,2
;

给予:

CUST_ID   TXN_ID   TXN_DATE                  C__PRIOR_90_DAYS_TRANSACTION
0         0        2020-12-04 00:00:00.000   0
1         1        2020-12-03 00:00:00.000   3

问题中没有明确定义的是,如果 12 月份有很多客户的请求,该怎么办 12月同一天有多笔交易怎么办。

以上将为每位客户的每笔 12 月交易返回一行,其中包括发生在同一天的交易。但是如果你的日期/时间戳有时间,那么它只会计算同一天早些时候的转换。 但是,如果您想要前几天并且 txn_date 只是一个日期,那么

AND t.txn_date >= dt.win_start and t.txn_date < dt.win_end AND t.txn_id != dt.txn_id

应该使用。

如果 txn_date 是时间戳,则 dec_txn 应更改为:

dec_txn AS (
    SELECT txn_id,
        cust_id,
        DATEADD('day',-90, txn_date::date) AS win_start,
        txn_date::date AS win_end
    FROM data 
    WHERE date_trunc('month', txn_date) = '2020-12-01'

现在窗口时间戳被截断为天,如果您希望午夜交易计入当天,或者如果您没有午夜时间戳,那么您将不得不锻炼......