我的任务是: 变量xdef分别是计数器和布尔值.两者都从零开始.

  1. 如果在任何时间点x > 2,则从该点开始def = 1.
  2. 现在,如果是def = 1,那么为了使它再次变为零,x必须在连续3个周期内等于0,否则def仍然是1.

我有SAS的背景,现在开始更广泛地使用PostgreSQL:

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 $f$
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 $f$;

推荐答案

Pl/pgSQL可以做到这一点,但是您不需要它的复杂性和开销来实现您所需要的:使用window functions在普通的声明性SQL:demo中完全可以做到

  1. 0=all(array[])判断数组中的所有元素是否都为0
  2. lag(x,that_many_rows_back,0)从返回的许多行中获取值x.或者0,如果在这之前没有那么多行的话.
  3. 您还可以使用every()bool_and()作为窗口函数,并将其与FRAME START子句结合使用,以使窗口框架仅向后到达2行.
  4. 您可以原地定义窗口,也可以在over()条款中定义窗口,或者在window条款中单独定义窗口.如果它很重要,或者您需要重新使用它,那么后者是更好的 Select .
  5. bool_or()判断直到该值的任何值是否满足条件.在这里,它仅限于一个窗口框架,因此它只关注自上次defx中得到3个零后关闭的行.
  6. boolean::int castfalse映射到0,true映射到1.
with cte as (
    select *
      ,(x > 2) exceeded_2
      ,(every(x=0)over w1) as should_switch
    from have
    window w1 as (order by n rows 2 preceding) )
,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 def
1 0 0
2 1 0
3 2 0
4 3 1 first x>2
5 4 1
6 2 1
7 1 1
8 0 1
9 1 1
10 0 1
11 0 1
12 0 0 third zero in x
13 0 0
14 1 0
15 2 0
16 3 1 x>2
17 4 1

Sql相关问答推荐

跨多列的PostgreSQL非不同对

解析键-值对,根据值 Select ,并使用SQL创建新列

如何在Presto中将多个列合并到一个数组中

SQL:如何将相应位置的两个数组中的元素组合在一起

DBeaver将过程中的属性列表转换为字符串

如何将`now()`作为SQL插入语句的一部分?

SQL按日期分组字段和如果日期匹配则求和

比较SQL中以逗号分隔的字符串

存储过程太慢

根据日期 Select ID 的上一条记录

日期逻辑(查找过go 90 天内的第一个匹配行)

给定 3 个键列,从一个表中 Select 另一表中不存在的所有数据

如何在第二个 INSERT 中使用第一个 INSERT 自动生成的 ID

使用SQL中另一个表的查询结果在表中查找记录

Oracle SQL 从多个条件中 Select 但具有相同的 id

Postgres:表的累积视图

PlSql 陷入死循环

使用 JSON_BUILD_OBJ 从 Postgres 返回 JSON

当计数为 0 时显示行

如何刷新在视图之上创建的表