如何根据同一列中的前一行值计算行值

How to calculate a row value based on the previous row value in the same column

我有以下数据集:

     DATE   CODE    RANK    PARTITION
       ?    ABS        0           1
 12/04/2014 RET        1           1
 20/04/2014 RET        2           1
 01/05/2014 ABS        2           1
 13/05/2014 RET        2           1
 01/06/2015 ABS        2           1
 09/10/2015 RETk       2           1
         ?  ABS        0           2
 02/04/2015 RET        1           2
 03/04/2015 RET        2           2
 04/04/2015 ABS        2           2
 05/04/2015 STT        3           2
 06/04/2015 RETk       4           2
 07/04/2015 RETk       4           2

RANK 是我要在我的 SQL 中计算的列,给定列 DATE、CODE 和同一列的先前值。这里初始化为0。 我要实现的逻辑如下:

 If RANK-1 (previous row) IS NULL AND CODE = ABS THEN RANK = 0
 If RANK-1 (previous row) IS NULL AND CODE <> ABS THEN RANK <- (RANK-1) + 1
 If RANK-1 = 0 or 1 AND CODE = RET THEN RANK <-  (RANK-1) + 1
 If RANK-1 = 2 AND CODE = STT THEN RANK <- (RANK-1) + 1
 If RANK-1 = 3 AND CODE = RETk THEN RANK <-  (RANK-1) + 1
 If CODE = ABS THEN RANK <- (RANK-1) (previous row)
 Else 0

我使用的 Teradata 版本是 R14。如上例所示,计算是在分区基础上完成的。我在模型中添加了更多约束以使其更清晰。在这个例子中,如果当前代码是 RET,我不会增加排名,直到前一个代码为 0 或 1。同样,如果我当前代码是 RETk,我不会增加排名,直到前一个代码等于 3,否则,我不会更改排名。我在下面的分区中重复相同的过程等等...

我不知道如何更新当前列值给定的前一列...我尝试了很多使用 OLAP 函数的逻辑实现,但都没有成功。 谁能给我一个提示?

非常感谢您的帮助

大致如下:

create table table1
(
  datecol date,
  code varchar(10),
  rankcol integer
);     
--insert into table1 select '2014/05/13', 'RETj', 0;

select 
  case 
  when s1.code='ABS' and s2.rankcol = 1 then 1
  when s1.code='RET' and s2.rankcol = 0 then 1
  when s1.code='RET' and s2.rankcol = 1 then 2
  else 0
  end RET_res,
  s1.*, s2.*
from
(select rankcol, code, row_number() OVER (order by datecol) var1 from table1) s1,
(select rankcol, code, row_number() OVER (order by datecol) var1 from table1) s2
where s1.var1=s2.var1-1
order by s1.var1  
; 

您始终可以对此类任务使用递归查询。但是除非每组的行数很低,否则性能会很差。

首先你需要一种前进到下一行的方法,因为不能根据当前行的日期计算下一行的日期,你必须具体化数据并添加 ROW_NUMBER:

CREATE TABLE tab(dt DATE, CODE VARCHAR(10), rnk INT, part INT);

INSERT INTO tab(            NULL,'ABS' ,0 ,          1);
INSERT INTO tab(DATE'2014-04-12','RET' ,1 ,          1);
INSERT INTO tab(DATE'2014-04-20','RET' ,2 ,          1);
INSERT INTO tab(DATE'2014-05-01','ABS' ,2 ,          1);
INSERT INTO tab(DATE'2014-05-13','RET' ,2 ,          1);
INSERT INTO tab(DATE'2014-06-01','ABS' ,2 ,          1);
INSERT INTO tab(DATE'2014-10-09','RETk',2 ,          1);
INSERT INTO tab(            NULL,'ABS' ,0 ,          2);
INSERT INTO tab(DATE'2015-04-02','RET' ,1 ,          2);
INSERT INTO tab(DATE'2015-04-03','RET' ,2 ,          2);
INSERT INTO tab(DATE'2015-04-04','ABS' ,2 ,          2);
INSERT INTO tab(DATE'2015-04-05','STT' ,3 ,          2);
INSERT INTO tab(DATE'2015-04-06','RETk',4 ,          2);
INSERT INTO tab(DATE'2015-04-07','RETk',4 ,          2);

CREATE VOLATILE TABLE vt AS
 (
   SELECT dt, code, part
     -- used to find the next row
     ,ROW_NUMBER() OVER (PARTITION BY part ORDER BY dt) AS rn
   FROM tab
 ) WITH DATA
PRIMARY INDEX(part, rn)
ON COMMIT PRESERVE ROWS
;

现在它只是在一行又一行地使用 CASE 应用您的逻辑:

WITH RECURSIVE cte (dt, code, rnk, part, rn) AS
 (
   SELECT
      dt
     ,code
     ,CASE WHEN code = 'ABS' THEN 0 ELSE 1 END
     ,part
     ,rn
   FROM vt
   WHERE rn = 1
   UNION ALL
   SELECT
      vt.dt
     ,vt.code
     ,CASE
         WHEN cte.rnk IN (0,1) AND vt.CODE = 'RET'  THEN cte.rnk + 1
         WHEN cte.rnk = 2      AND vt.CODE = 'STT'  THEN cte.rnk + 1
         WHEN cte.rnk = 3      AND vt.CODE = 'RETk' THEN cte.rnk + 1
         WHEN                      vt.CODE = 'ABS'  THEN cte.rnk
         ELSE cte.rnk
      END
     ,vt.part
     ,vt.rn
   FROM vt JOIN cte
     ON vt.part =cte.part
    AND vt.rn   =cte.rn + 1  
 ) 
SELECT *
FROM cte
ORDER BY part, dt;

但我认为你的逻辑实际上不是这样的(基于前几行的确切 RANK 值),你只是陷入了程序思维:-)

您也许可以只使用 OLAP 函数来做您想做的事情...