在同一个表格中获取最后一个非空值

时间:2017-11-24 03:17:44

标签: sql postgresql

我有下表和数据。

create table items
(
    itemid int
    ,userid varchar(10)
    ,itemtype varchar(10)
    ,value varchar(10)
);

insert into items values (101,'usr1','CST','');
insert into items values (101,'usr1','GST','');

insert into items values (100,'usr1','Data','a25');
insert into items values (100,'usr1','GST','');

insert into items values (99,'usr3','Data','a50');

insert into items values (98,'usr3','CST','');
insert into items values (98,'usr3','GST','');

insert into items values (97,'usr3','CST','');

insert into items values (96,'usr3','Data','a25');
insert into items values (96,'usr3','GST','');

insert into items values (95,'usr3','Data','a50');
insert into items values (95,'usr3','GST','');

这是一个发票行表,其中包含项目中每行的详细信息。某些发票行没有“数据”类型的行。对于这些记录,我们需要遍历该表并通过匹配userid找到其中包含“Data”值的 next lower itemid ,获取该值并将其作为输出。

这是预期的输出 -

itemid,userid,itemtype,value
101,usr1,CST,a25
101,usr1,GST,a25
98,usr3,CST,a25
98,usr3,GST,a25
97,usr3,CST,a25

可以看出,itemid 101从itemid 100中获取值。同样,itemid的98和97从itemid 96获得了valeus。

我编写了以下查询以获取所有不包含数据的发票 -

;with cte_groupdata
as
(
    select  itemid
            ,userid
            ,case 
                when itemtype = 'Data' then 1
                else 0
            end as rn
    from    items
    group by itemid
            ,userid
            ,case 
                when itemtype = 'Data' then 1
                else 0
            end
)
,cte_validdata
as
(
    select  itemid
            ,userid
            ,count(*) as total
    from    cte_groupdata
    group by itemid
            ,userid
)
select  vld.itemid
        ,vld.userid
        ,it.itemtype
from    cte_validdata vld
        join items it
            on vld.userid = it.userid
            and vld.itemid = it.itemid
where   vld.total = 1
            and it.itemtype <> 'Data';

我收到了我需要处理的所需发票。我知道我需要编写一个共同相关的子查询。我只是无法理解如何设置条件。这是一个prod数据,我们没有创建UDF或过程的权限。

2 个答案:

答案 0 :(得分:2)

使用cross join lateral就足够了:

create table items
(
    itemid int
    ,userid varchar(10)
    ,itemtype varchar(10)
    ,value varchar(10)
);
insert into items values
 (101,'usr1','CST',''),
 (101,'usr1','GST',''),
 (100,'usr1','Data','a25'),
 (100,'usr1','GST',''),
 (99,'usr3','Data','a50'),
 (98,'usr3','CST',''),
 (98,'usr3','GST',''),
 (97,'usr3','CST',''),
 (96,'usr3','Data','a25'),
 (96,'usr3','GST',''),
 (95,'usr3','Data','a50'),
 (95,'usr3','GST','');
select
       i.itemid, i.userid, i.itemtype, cjl.datavalue
from items i
cross join lateral (
    select value as datavalue
    from items i2
    where i.userid = i2.userid 
    and i.itemid > i2.itemid 
    and i2.itemtype = 'Data'
    and i2.value <> ''
    order by i2.itemid desc
    limit 1
    ) cjl
where i.itemtype <> 'Data'
itemid | userid | itemtype | datavalue
-----: | :----- | :------- | :--------
   101 | usr1   | CST      | a25      
   101 | usr1   | GST      | a25      
    98 | usr3   | CST      | a25      
    98 | usr3   | GST      | a25      
    97 | usr3   | CST      | a25      
    96 | usr3   | GST      | a50      

dbfiddle here

答案 1 :(得分:1)

您可以尝试NOT EXISTS子句将行过滤到没有数据的行,然后将其与包含相关子查询的联接组合以获取缺少的数据:

select i.itemid, i.userid, i.itemtype,
       iprev.value
  from items i
  join items iprev
    on iprev.userid = i.userid
   and iprev.itemtype = 'Data'
   and iprev.value <> ''
   and iprev.itemid = (select max(i2.itemid)
                         from items i2
                        where i2.itemid < i.itemid
                          and i2.userid = iprev.userid
                          and i2.itemtype = iprev.itemtype
                          and i2.value <> '')
 where i.value = ''
   and not exists (select null
                     from items i2
                    where i2.itemid = i.itemid
                      and i2.userid = i.userid
                      and i2.value <> '')