【问题标题】:Teradata SQL: Calculate running totals if a condition is metTeradata SQL:如果满足条件,则计算运行总计
【发布时间】:2020-04-09 11:07:42
【问题描述】:

我有一个包含以下列和数据的数据集:

Customer | Week_number | Amount
cust1    |  0          | 100
cust1    |  1          | 200
cust1    |  3          | 300
cust2    |  0          | 1000
cust2    |  1          | 2000

我需要为每个客户计算每两周的总数。

使用窗口函数,我可以做到这一点:

SELECT 
 CUSTOMER, WEEK_NUMBER
, SUM(AMOUNT) OVER (PARTITION BY CUSTOMER ORDER BY WEEK_NUMBER ROWS 1 PRECEDING) AS FORTNIGHT_AMOUNT
FROM AMOUNT

但是,即使前一周没有金额,这也会增加金额。在上面的示例中,对于第 3 行的 cust1,它将第 3 周和第 1 周相加。只有当 week_number 比当前行的周数小 1 时,才应添加金额。这可能吗?感谢您的帮助。

我得到了什么:

Customer | Week_number | Fortnight_Amount
cust1    |  0          | 100
cust1    |  1          | 300
cust1    |  3          | **500**
cust2    |  0          | 1000
cust2    |  1          | 3000

预期结果:

Customer | Week_number | Fortnight_Amount
cust1    |  0          | 100
cust1    |  1          | 300
cust1    |  3          | **300**
cust2    |  0          | 1000
cust2    |  1          | 3000

【问题讨论】:

  • 请向我们展示您的预期结果。
  • 如果有cust1week_number``4 的记录怎么办?您会重置sum,还是继续将其添加到正在运行的sum
  • 如果有一个第 4 周(比如 amount=400),那么第 4 周的 fortnight_amount 将为 700(第 4 周 + 第 3 周)
  • 对于不会说英语的人来说,两周是两周。

标签: sql teradata


【解决方案1】:

如果您只想忽略不立即连续的周数,您可以先使用lag(),然后再创建一个窗口sum()

select
    customer,
    week_number,
    sum(
        case when lag_week_number is null or week_number = lag_week_number + 1 
            then amount
            else 0 
        end
    ) over(partition by customer order by week_number) fortnight_amount
from (
    select 
        t.*, 
        lag(week_number) over(partition by customer order by week_number) lag_week_number
    from mytable t
) t

实际上,您可能真的想重置 sum,当 week_numbers 存在差距时。为此,这是某种间隙和孤岛分配,您将采用不同的方式进行操作:想法是当两个连续的周数按顺序排列时进行累积 sum 以开始一个新组,然后在每个组内求和:

select 
    customer,
    week_number,
    sum(amount) over(partition by customer, grp order by week_date) fortnight_amount
from (
    select 
        t.*,
        sum(
            case 
                when lag_week_number is null or week_number = lag_week_number + 1 
                then 0
                else 1
            end
        ) grp
    from (
        select 
            t.*, 
            lag(week_number) over(partition by customer order by week_number) lag_week_number
        from mytable t
    ) t
) t

【讨论】:

  • 感谢您的回答。我一直在尝试运行您提供的第一个答案,但它失败并出现错误“数据类型“WEEK_NUMBER”与定义的类型名称不匹配”。 WEEK_NUMBER 是一个整数..
【解决方案2】:

您想要range 分区,而不是row 分区:

SELECT CUSTOMER, WEEK_NUMBER,
       SUM(AMOUNT) OVER (PARTITION BY CUSTOMER
                         ORDER BY WEEK_NUMBER 
                         RANGE BETWEEN 1 PRECEDING AND CURRENT ROW
                        ) AS FORTNIGHT_AMOUNT
FROM AMOUNT;

【讨论】:

    【解决方案3】:

    感谢@Gordon 和@GMB 的回答。不幸的是,我无法在 Teradata SQL 中同时使用 LAG 函数或 RANGE 分区。但我能够使用你们所描述的概念来获得以下答案。

    SELECT 
    CUSTOMER
    , WEEK_NUMBER
    , LAG_WEEK_NUMBER
    , AMOUNT
    , CASE 
      WHEN WEEK_NUMBER = LAG_WEEK_NUMBER + 1 
      THEN SUM(AMOUNT) OVER (PARTITION BY CUSTOMER ORDER BY WEEK_NUMBER ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
      ELSE AMOUNT
    END AS TWO_WEEK_SUM_AMOUNT
    FROM (
      SELECT 
      T.*
      , MAX(WEEK_NUMBER) OVER (PARTITION BY CUSTOMER ORDER BY WEEK_NUMBER ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS LAG_WEEK_NUMBER
      FROM MY_TABLE T
      ) T
    ORDER BY CUSTOMER, WEEK_NUMBER
    

    我能够从@dnoeth 在这些链接中的回答中获得 Teradata 中的 LAG 函数实现:

    MAX(WEEK_NUMBER) OVER (PARTITION BY CUSTOMER ORDER BY WEEK_NUMBER ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS LAG_WEEK_NUMBER
    

    rows between 1 preceding and preceding 1

    Teradata partitioned query ... following rows dynamically

    如果您发现答案有任何问题或是否可以通过任何方式改进,请告诉我。

    【讨论】:

      【解决方案4】:

      如果只有两周/行,您的查询可以进一步简化为解释中的单个 STATS 步骤(因为两个 OLAP 函数都应用相同的 PARTITION/ORDER):

      SELECT T.*
      , CASE 
          WHEN MAX(WEEK_NUMBER) OVER (PARTITION BY CUSTOMER ORDER BY WEEK_NUMBER ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) + 1 = WEEK_NUMBER
          THEN SUM(AMOUNT)      OVER (PARTITION BY CUSTOMER ORDER BY WEEK_NUMBER ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
         ELSE AMOUNT
        END AS TWO_WEEK_SUM_AMOUNT
      FROM MY_TABLE T
      ORDER BY CUSTOMER, WEEK_NUMBER
      

      当然,这假设周从 0 开始,并且没有上一年的第 52/53 周。

      【讨论】:

      • 有没有一种方法可以用来计算超过 4 周的运行总数。条件是相同的。一周可能有也可能没有总数,只有在以下情况下才应将总数相加周数介于当前周和比当前周少 3 之间。谢谢..
      • 您可以添加对前三行的检查并添加这一行(看起来很难看,但应该很有效):CASE WHEN MAX(WEEK_NUMBER) OVER (PARTITION BY CUSTOMER ORDER BY WEEK_NUMBER ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) BETWEEN WEEK_NUMBER - 3 AND WEEK_NUMBER THEN SUM(AMOUNT) OVER (PARTITION BY CUSTOMER ORDER BY WEEK_NUMBER ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) ELSE 0 END + ... 对前面的第 2 行和第 3 行重复 CASE。如果您使用的是 TD16.10+,则可以使用 LAG 对其进行简化。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-15
      • 1970-01-01
      • 2017-10-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多