过去 30 天的平均值,不包括当前记录(混合日期和基于行的条件)

Posted

技术标签:

【中文标题】过去 30 天的平均值,不包括当前记录(混合日期和基于行的条件)【英文标题】:Average over last 30 days not including current record (mixed date and row-based condition) 【发布时间】:2019-06-25 15:16:30 【问题描述】:

我正在编写一个名为 CRD_TRX_SYAMT_AVG_30d 的专栏,该专栏符合以下条件:

根据TRXHOSTDATETIME计算过去30天SYSTEMAMOUNT列的平均值 排除 RESPONSECODE 不是 00 的行 排除当前记录

下面有完整的示例数据。我已经将它粘贴到 DB Fiddle 中,但是有一个问题阻止代码在那里运行。对我来说,它在 SQL Developer 中运行良好,但结果出乎意料。

我尝试用 1 个 PRECEDING 代替 CURRENT ROW,但根据我的理解,不可能混合使用间隔和范围。 CRD_TRX_SYAMT_AVG_30_trx_exc 列使用基于行的条件(ROWS BETWEEN 30 PRECEDING 和 1 PRECEDING),这很好用。所以问题只在于CRD_TRX_SYAMT_AVG_30d

如何将条件更改为以下内容?

RANGE BETWEEN INTERVAL '30' DAY PRECEDING AND 1 PRECEDING

https://dbfiddle.uk/?rdbms=oracle_18&fiddle=471da47ef5df960b67e427053e7642d4

-- DATE formatting
alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';

-- DROP and CREATE TABLE
DROP TABLE TMP_EXA_RSS_ISS_AUT_S_100CRD_T;

CREATE TABLE "RAI"."TMP_EXA_RSS_ISS_AUT_S_100CRD_T" 
 (  "ID_KEY_HASH" VARCHAR2(10), 
"TRXHOSTDATETIME" DATE, 
"SYSTEMAMOUNT" NUMBER(10,1), 
"RESPONSECODE" CHAR(2)
 );

 -- fill entire table
DECLARE
  nTRX NUMBER(10) := 100;
BEGIN
FOR i IN 1 .. nTRX LOOP
  INSERT INTO tmp_exa_RSS_ISS_AUT_S_100CRD_T VALUES
  (
    FLOOR(DBMS_RANDOM.VALUE (1, 4)), -- ID_KEY_HASH
    TO_DATE('01.01.2018', 'DD.MM.YYYY') + dbms_random.value(0, 60), -- TRXHOSTDATETIME
    FLOOR(DBMS_RANDOM.VALUE (1, 10)) * 100, -- SYSTEMAMOUNT
    CASE round(dbms_random.value(1,11)) 
            WHEN 1 THEN '55' 
            WHEN 2 THEN '88'
            ELSE '00'
       END -- RESPONSECODE 
  );
END LOOP;
END;
/

-- review all data created
SELECT * FROM tmp_exa_RSS_ISS_AUT_S_100CRD_T ORDER BY ID_KEY_HASH, TRXHOSTDATETIME;


-- example features:

SELECT ID_KEY_HASH, TRXHOSTDATETIME, SYSTEMAMOUNT, RESPONSECODE,
ROUND(AVG(
  CASE WHEN ((SYSTEMAMOUNT > 0) AND (RESPONSECODE = '00')  ) THEN SYSTEMAMOUNT ELSE NULL END
) OVER (PARTITION BY ID_KEY_HASH ORDER BY TRXHOSTDATETIME 
                        RANGE BETWEEN INTERVAL '30' DAY PRECEDING AND CURRENT ROW), 2)  AS CRD_TRX_SYAMT_AVG_30d,
NVL( ROUND( 
  AVG(
      CASE WHEN ((SYSTEMAMOUNT > 0) AND (RESPONSECODE = '00')  ) THEN SYSTEMAMOUNT ELSE NULL END
      ) 
      OVER (PARTITION BY ID_KEY_HASH ORDER BY TRXHOSTDATETIME NULLS LAST 
      ROWS BETWEEN 30 PRECEDING AND 1 PRECEDING)
  , 2), 0 ) AS CRD_TRX_SYAMT_AVG_30_trx_exc
FROM tmp_exa_RSS_ISS_AUT_S_100CRD_T
ORDER BY ID_KEY_HASH, TRXHOSTDATETIME
;

【问题讨论】:

向我们展示数据库架构、示例数据、当前和预期输出。请阅读How-to-Ask 这里是START 了解如何提高问题质量并获得更好答案的好地方。 How to create a Minimal, Complete, and Verifiable example 尝试在rextester.com 中创建样本 而不是 FOR .. LOOP 只使用 INSERT,但给我们一个工作示例,否则我们无法测试它 我已经为你修好了小提琴:dbfiddle.uk/… 但我认为这个小提琴不适合测试一个像这样的 hudreds 行的大型演示,因为它只显示最大。 10 行。 使用随机值的问题是我们不知道结果应该是什么;如果您提供了固定插入,那么您可以包括您的预期结果,并有一些更具体的目标和测试。你可能只想要RANGE BETWEEN 30 PRECEDING AND 1 PRECEDING,但目前很难说。 怎么样:range between interval '30' day preceding and interval '1' second preceding?只要您不能为同一 ID_KEY_HASH 重复日期时间,这应该可以工作。 【参考方案1】:

如果复合 (ID_KEY_HASH, TRXHOSTDATETIME) 是唯一的(同一 ID_KEY_HASH 没有重复的日期时间),则以下表达式将满足您的要求:

 avg(case when systemamount > 0 and responsecode = '00' then systemamount end)
   over (partition by id_key_hash order by TRXHOSTDATETIME 
         range between interval '30' day preceding 
                   and interval '1' second preceding)

如果需要,您可以将其包装在 ROUND( ... , 2) 中。

【讨论】:

我认为这个假设适用于我的用例。我将您的解决方案放在 DB Fiddle 上:dbfiddle.uk/…。我不知道为什么,但是随机数生成器的种子不起作用。

以上是关于过去 30 天的平均值,不包括当前记录(混合日期和基于行的条件)的主要内容,如果未能解决你的问题,请参考以下文章

查看过去 30 天的所有使用情况,按用户和滚动分组

过去 30 天的移动平均线

在 where 子句中从当前日期减去 30 天

根据日期(过去 30 天)获取记录 [重复]

如何使用窗口函数获取每个日期值的今天、过去 7 天、过去 30 天的指标?

提取过去 30 天的记录