如何不将 where 子句应用于窗口函数

Posted

技术标签:

【中文标题】如何不将 where 子句应用于窗口函数【英文标题】:How to not apply a where clause to a window function 【发布时间】:2021-09-05 10:57:03 【问题描述】:

我有一个表格显示客户的购买情况,如下所示:

day,transaction_id,customer_id,price
2020-01-01,1,100,10
2020-01-02,2,200,20
2020-01-03,3,100,30
2020-01-04,4,200,40
2020-02-01,5,100,50
2020-02-02,6,200,60
2020-02-03,7,100,70
2020-02-04,8,200,80

我想要一个查询,显示每个用户迄今为止所有交易的总和,但仅限于 2020 年 2 月的交易。

如果我执行以下操作,那么我相信只有 2 月份的交易会在窗口函数中进行评估:

SELECT 
    transaction_id,
    SUM(price) OVER (PARTITION BY customer_id ORDER BY day) AS total
FROM 
    sales
WHERE 
    day BETWEEN DATE '2021-02-01' AND DATE '2021-02-28'

如何仅计算二月份的行的窗口函数,但在窗口函数计算中包含所有先前的行?

这只是示例数据,但在实际数据中,有大量的销售额,因此对于指定时间范围之外的所有行,都有大量的窗口函数的冗余计算。

编辑

我认为我的问题被误解了,因为到目前为止这两个答案都与我想要的相反。

我只想返回 2 月的行程,但窗口函数应该计算 1 月和 2 月的ALL次行程。

结果应该是这样的:

transaction_id,total
5,90
6,120
7,160
8,200

【问题讨论】:

【参考方案1】:

我想要一个查询,显示每个用户迄今为止所有交易的总和,但仅限于 2020 年 2 月的交易。

如果我理解正确,您只想显示 2 月份的交易,但您希望总和为所有交易。如果是这样,则使用子查询:

select s.*
from (select s.*,
             sum(price) over (partition by customer_id order by day) as running_price
      from sales s
     ) s
where day >= date '2021-02-01' and date < date '2021-03-01';

注意使用月份中的第一个和不等式进行日期比较。这适用于 2020 年(闰年)和 2021 年(非闰年)——具有讽刺意味的是,您的问题中都提到了这两年。

【讨论】:

谢谢。我编辑了我的问题以进行澄清,但是是的,您的理解是正确的。 SQL 引擎会首先为表中的所有行计算窗口函数,然后应用 where 子句,还是会足够智能地知道外部查询只希望为 2 月份的行计算窗口函数? @KOB。 . .我很确定它会计算所有行的值。 有趣,这确实是我问题的重点。在我的实际用例中,我在查询中有几个计算量很大的窗口函数,而且我的表很大。当我只想返回不到 1% 的冗余计算时,这只是执行了大量的冗余计算【参考方案2】:

使用条件求和:

SELECT 
    transaction_id,
    SUM(CASE WHEN day BETWEEN DATE '2021-02-01' AND DATE '2021-02-28'
             THEN price ELSE 0 END) OVER (PARTITION BY customer_id ORDER BY day) AS total
FROM sales

【讨论】:

【参考方案3】:

我想我明白了你的问题。我使用 T-SQL 并创建了临时表和子查询

declare @from as date ='2020-02-01'
declare @to as date='2020-02-28'
select * from sales
select price, 
    (SELECT SUM(b.price) 
    FROM sales b 
    WHERE b.customer_id = a.customer_id and b.transaction_id <= a.transaction_id) as runningTotal,
    transaction_id, 
    customer_id, 
    day 
INTO #Temp from sales a

select * from #Temp where [day] between @from and @to order by customer_id, transaction_id
drop table #temp

enter image description here

【讨论】:

以上是关于如何不将 where 子句应用于窗口函数的主要内容,如果未能解决你的问题,请参考以下文章

在spark sql中对窗口函数使用having子句的语义是什么?

Oracle分析函数

Django ORM:带后续过滤的窗口函数

where 子句中的函数调用

如何将 groupBy 和聚合函数应用于 PySpark DataFrame 中的特定窗口?

sql查询语句并不是最先执行SELECT