如何制作自参照窗口功能

时间:2019-09-05 16:46:47

标签: postgresql window-functions

我有一张这样的桌子:

amount  type  app  owe
1       a     10   10
2       a      8   -2
3       a     20   12
4       i     30   10
5       a     40   10

欠款额是:

(type == 'a')?app - sum(owe) where amount < (amount for current row):max(app-sum(owe)where amount<(amount for current row),0)

所以我需要在窗口功能所在的列上有一个窗口功能。在没有限制的前一行和前一行之间的行之间的行上有这些分区,但是它必须在不同的列上,而不是我正在累加的列上。有没有办法引用窗口功能所在的同一列

我尝试了一个别名

case 
  when type = a 
    then app - sum(owe)over(ROWS BETWEEN UNBOUNDED PRECEDING AND 1 preceding) as owe
  else 
    greatest(0,app - sum(owe)over(ROWS BETWEEN UNBOUNDED PRECEDING AND 1 preceding)) 
end as owe

但是由于欠债在我付清时不存在,所以我得到:

  

欠费不存在。

还有其他方法吗?

2 个答案:

答案 0 :(得分:0)

您无法使用窗口功能执行此操作。使用SQL的唯一机会是递归CTE:

WITH RECURSIVE tab_owe AS (
   SELECT amount, type, app,
          CASE WHEN type = 'a'
               THEN app
               ELSE GREATEST(app, 0)
          END AS owe
   FROM tab
   ORDER BY amount LIMIT 1
UNION ALL
   SELECT t.amount, t.type, t.app,
          CASE WHEN t.type = 'a'
               THEN t.app - sum(tab_owe.owe)
               ELSE GREATEST(t.app - sum(tab_owe.owe), 0)
          END AS owe
   FROM (SELECT amount, type, app
         FROM tab
         WHERE amount > (SELECT max(amount) FROM tab_owe)
         ORDER BY amount
         LIMIT 1) AS t
      CROSS JOIN tab_owe
   GROUP BY t.amount, t.type, t.app
)
SELECT amount, type, app, owe
FROM tab_owe;

(未经测试)

用程序代码编写起来会容易得多,因此请考虑使用表函数。

答案 1 :(得分:0)

这是我想出的。当然,我不是一个真正的程序员,所以我确定有一种更聪明的方法:

    insert into mort (amount, "type", app)
values
(1,'a',10),
(2,'a',8),
(3,'a',20),
(4,'i',30),
(5,'a',40)
CREATE OR REPLACE FUNCTION mort_v ()
  RETURNS TABLE (
     zamount int,
     ztype text,
       zapp int,
       zowe double precision
) AS $$
DECLARE
   var_r record;
    charlie double precision;
    sam double precision;
BEGIN
  charlie = 0;
  FOR var_r IN(SELECT
                 amount,
                 "type",
                app

               FROM mort order by 1)
    LOOP
       zamount = var_r.amount;
     ztype = var_r.type;
                zapp = var_r.app;
                sam = var_r.app - charlie;

                if ztype = 'a' then
                zowe = sam;
                else
                zowe = greatest(sam, 0);
                end if;
                charlie = charlie + zowe;
       RETURN NEXT;
  END LOOP;
END; $$
LANGUAGE 'plpgsql';
select * from mort_v()

因此,由于我的技能有限,您会发现我不得不在表中已存在的列前面添加一个“ z”,以便再次将其吐出。如果表有30列,则通常必须执行30次。但是,我问了一位真正的工程师,他提到如果您只将主键与计算出的列一起吐出,就可以将其重新连接到原始表中。那比我拥有的要聪明。如果有更好的解决方案,那就太好了。确实可以很好地说明如何在postgre中执行类似游标的操作,以及如何在mssqlserver中像前面那样在不使用'@'的情况下进行变量。