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

问题描述 投票:0回答:1

我的任务: 变量

x and def
分别是计数器和布尔值。两者都从零开始。如果在任何时间点
x > 2 then def = 1
。现在
if def = 1
为了再次变为零,
x
必须连续 3 个周期等于 0,否则它仍然等于 1。 我有 sas 背景,现在开始更广泛地使用 Postgresql。有人可以帮我在sql中执行以下操作吗?

data have;
input n x;
datalines;
1 0
2 1
3 2
4 3
5 4
6 2
7 1
8 0
9 1
10 0
11 0
12 0
13 0
14 1
15 2
16 3
17 4
;
run;


data want(drop=prev_: cur_m def rename=def_new=def);
set have;
retain  prev_def prev_cur_m;
if x > 2 then def = 1;
else if prev_def = 1 and x > 0 then def = 1;
else def = 0;

if _n_ = 1 then cur_m = -1;
else if def = 1 then cur_m = -1;
else if prev_def = 1 and def = 0 then cur_m = 0;
else if -1 < prev_cur_m < 3 and x > 0 then cur_m = 0;
else if -1 < prev_cur_m < 3 and x = 0 then cur_m + 1;
else cur_m = -1;

if _n_ = 1 then def_new = 0;
else if def = 1 then def_new = 1;
else if -1 < cur_m < 3 then def_new = 1;
else def_new = 0;

output;
prev_def = def;
prev_cur_m = cur_m;
run;

据我了解,这可以通过游标来实现,并尝试使用此post解决问题。然而,当我需要使用前一行的值来计算当前的值时,我陷入了困境。这是代码:

create table have (
    n int,
    x int
) ;
insert into have (n,x)
values (1,0),(2,1),(3,2),(4,3),(5,4),(6,2),(7,1),(8,0),(9,1),(10,0),(11,0),(12,0),(13,0),(14,1),(15,2),(16,3),(17,4)

create or replace function calc_dpd()
returns table (
    n int,
    x int,
    def int,
    cure_m int,
    def_new int
)
language plpgsql as
$$
declare 
    rec record;
begin
    for rec in (select * from have order by n) loop 
        if rec.n = 1 then 
            def = 0;
            cure_m = 0;
            def_new = 0;
        end if;
        if x > 2 then 
            def = 1;
        end if;

--  here I need to test if def from previous row = 1 and current def = 0;

        select rec.n, rec.x
        into n,x;
        return next;
    end loop;
end
$$

postgresql sas
1个回答
0
投票

PL/pgSQL 功能强大,您不需要所有的功能、复杂性和开销来实现您所需要的:这一切都可以通过简单的声明性 SQL 中的窗口函数实现:演示

  1. 0=all(array[])
    检查数组中的所有元素是否为
    0
  2. lag(x,that_many_rows_back,0)
    从那么多行中获取值
    x
    。或者
    0
    如果这一行之前没有那么多行。
  3. 您可以在
    over()
    子句中就地定义窗口,也可以在
    window
    子句中单独定义窗口。如果您需要重复使用,后者是更好的选择。
  4. bool_or()
    检查此值之前的任何值是否符合条件。这里它仅限于窗口框架,因此它只涉及自上次
    def
    x
    中获得 3 个零后关闭以来的行。
with cte as (
select *
  ,(x > 2) exceeded_2
  ,(0 = all(array[x, lag(x,1,0)over w1, lag(x,2,0)over w1])) as should_switch
from have
window w1 as (order by n) )
,cte2 as (
  select *,sum(should_switch::int)over(order by n) def_on_period from cte
)
select n,x,(bool_or(exceeded_2) over w2)::int as def
from cte2
window w2 as (partition by def_on_period order by n);
n x 定义
1 0 0
2 1 0
3 2 0
4 3 1 第一个x>3
5 4 1
6 2 1
7 1 1
8 0 1
9 1 1
10 0 1
11 0 1
12 0 0 x 中的第三个零
13 0 0
14 1 0
15 2 0
16 3 1 x>3
17 4 1
© www.soinside.com 2019 - 2024. All rights reserved.