【问题标题】:Filtering within an window function (over ... partition by)?在窗口函数内过滤(超过...分区)?
【发布时间】:2013-07-20 02:01:31
【问题描述】:

我正在尝试使用 sum() over (partition by) 但在该求和中过滤。我的用例是对每个产品的过去 12 个月的总和,最多一个月的条目,所以:

ITEM    MONTH    SALES
Item A  1/1/2011     2
Item A  2/1/2011     5
Item A  3/1/2011     3
Item A  4/1/2011     7
Item A  5/1/2011    12
Item A  6/1/2011     8
Item A  7/1/2011     9
Item A  8/1/2011    15
Item A  9/1/2011     6
Item A  10/1/2011    7
Item A  11/1/2011   12
Item A  12/1/2011    1
Item A  1/1/2012     3
Item A  2/1/2012     4
Item A  3/1/2012     5
Item A  4/1/2012     6
Item A  5/1/2012     4
Item A  6/1/2012     8
Item A  7/1/2012     9
Item A  8/1/2012    12
Item A  9/1/2012    14
Item A  10/1/2012    8
Item A  11/1/2012   12
Item A  12/1/2012   16

然后会返回:

ITEM      MONTH_BEGIN SALES TTM SALES
Item A    1/1/2012        3        87
Item A    2/1/2012        4        88
Item A    3/1/2012        5        87
Item A    4/1/2012        6        89

其中 1/1/12 的 TTM SALES 是 1/1/11-12/1/11 的总和

【问题讨论】:

  • 你试过什么SQL?你有什么问题?
  • 您使用的是什么数据库软件和版本?它有助于用它标记您的问题,例如sql-server-2008.

标签: sql window-functions


【解决方案1】:

下面的查询显示了我将如何使用Oracle Analytic Functions

SELECT
   "ITEM",
   TO_CHAR("MONTH", 'MM/DD/YYYY') AS "MONTH_BEGIN",
   "SALES",
   SUM("SALES") OVER (
    PARTITION BY 
       "ITEM" 
    ORDER BY 
       "MONTH" 
    RANGE BETWEEN 
       INTERVAL '12' MONTH PRECEDING
       AND 
       INTERVAL '1' MONTH PRECEDING
   ) AS "TTM_SALES"  
FROM
   "SALES"  
ORDER BY
   "MONTH";

Working SQLFiddle demo


这将在当前行的月份前 12 个月开始并在其前 1 个月结束的窗口上计算 sum 函数。

我假设您不需要过滤 where 子句中的任何内容。如果这样做,请小心。引用Oracle documentation

分析函数是查询中执行的最后一组操作 除了最后的ORDER BY 子句。所有连接和所有WHEREGROUP BYHAVING 子句在分析函数完成之前完成 已处理。

假设您只想显示2012 年第一季度的结果;如果您尝试通过在 where 子句中进行过滤来做到这一点,它也会影响TTM_SALES 的累积结果(输出null3712)。

这里的底线是:如果您需要过滤掉分析函数窗口内的行,请将分析函数移动到子查询中,并按照@peterm 答案在外部查询中进行过滤:

SELECT 
   "X"."ITEM",
   TO_CHAR("X"."MONTH", 'MM/DD/YYYY') AS "MONTH_BEGIN",
   "X"."SALES",
   "X"."TTM_SALES"
FROM
(
   SELECT
      "ITEM",
      "MONTH",
      "SALES",
      SUM("SALES") OVER (
       PARTITION BY 
          "ITEM" 
       ORDER BY 
          "MONTH" 
       RANGE BETWEEN 
          INTERVAL '12' MONTH PRECEDING
          AND 
          INTERVAL '1' MONTH PRECEDING
      ) AS "TTM_SALES"  
   FROM
      "SALES"  
   ORDER BY
      "MONTH"
) "X"
WHERE 
  EXTRACT(MONTH FROM "X"."MONTH") BETWEEN 1 AND 4
  AND EXTRACT(YEAR FROM "X"."MONTH") = 2012; 

【讨论】:

    【解决方案2】:

    如果您对分析 SUM() 以外的任何内容持开放态度,那么这里有一个简单的相关子查询的可能解决方案

    SELECT s.item, s.month month_begin, s.sales,
           (SELECT SUM(sales) FROM sales 
             WHERE month BETWEEN DATEADD(month, -12, s.month) 
                             AND DATEADD(month,  -1, s.month)) ttm_sales
      FROM sales s 
     WHERE s.month BETWEEN '20120101' AND '20121201'
    

    样本输出:

    |项目 | MONTH_BEGIN |销售 | TTM_销售 | -------------------------------------------------- --------------- |项目 A | 2012 年 1 月 1 日 00:00:00+0000 | 3 | 87 | |项目 A | 2012 年 2 月 1 日 00:00:00+0000 | 4 | 88 | |项目 A | 2012 年 3 月 1 日 00:00:00+0000 | 5 | 87 | |项目 A | 2012 年 4 月 1 日 00:00:00+0000 | 6 | 89 | ...

    这里是SQLFiddle演示

    【讨论】:

    • 这么不文明... postgres 有min(...) filter(where ...) over(partition by ... order by...)
    猜你喜欢
    • 2018-02-24
    • 1970-01-01
    • 2018-06-09
    • 1970-01-01
    • 2018-07-19
    • 2011-06-29
    • 2016-04-10
    • 1970-01-01
    • 2021-05-18
    相关资源
    最近更新 更多