【问题标题】:Getting last non empty value within the same table在同一个表中获取最后一个非空值
【发布时间】:2017-11-24 03:17:43
【问题描述】:

我有下表和数据。

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找到下一个最低的itemid,其中有'Data'值,取值并将其作为输出。

这是预期的输出 -

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 获取值。

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

;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';

我收到了我需要进行处理的所需发票。我知道我需要编写一个相关的子查询。我只是无法理解如何提出条件。这是产品数据,我们无权创建 UDF 或程序。

【问题讨论】:

  • 有什么帮助吗?我是否缺少更多信息?

标签: sql postgresql


【解决方案1】:

使用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'
项目编号 |用户名 |项目类型 |数据值 -----: | :----- | :------- | :-------- 101 | usr1 |科技部 | a25 101 | usr1 |消费税 | a25 98 | usr3 |科技部 | a25 98 | usr3 |消费税 | a25 97 | usr3 |科技部 | a25 96 | usr3 |消费税 | a50

dbfiddle here

【讨论】:

    【解决方案2】:

    您可以尝试使用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 <> '')
    

    【讨论】:

    • 感谢@sstan 让我知道“不存在”子句。我不知道它也在postgre中。此查询适用于测试数据,但是当我在实际数据上运行它时,我遇到了内存不足错误。我通过连接其他三个表得出的表“项目”。我现在掌握了基本逻辑,并试图找出最好的解决方法。
    【解决方案3】:

    您应该在适当的条件下将表连接到自身。使用横向连接从连接的行中只获取一行:

    select 
        i1.itemid, i1.userid, i1.itemtype,
        case when i1.value <> '' then i1.value else i2.value end as value
    from items i1
    cross join lateral (
        select value
        from items i2
        where i1.userid = i2.userid 
        and i1.itemid > i2.itemid 
        and i2.value <> ''
        order by i2.itemid desc
        limit 1
        ) i2
    where i1.itemid in (
        select itemid
        from items
        group by itemid
        having string_agg(value, '') = '')
    order by i1.itemid desc, i1.userid, i1.itemtype;
    
     itemid | userid | itemtype | value 
    --------+--------+----------+-------
        101 | usr1   | CST      | a25
        101 | usr1   | GST      | a25
         98 | usr3   | CST      | a25
         98 | usr3   | GST      | a25
         97 | usr3   | CST      | a25
    (5 rows)    
    

    【讨论】:

    • 您的两个查询都没有考虑这样一个事实,即需要提取的数据应该首先不是空的,而不是全部。我在数据集中又添加了两行,结果不是预期的
    • 您可以通过使用附加条件来限制输出行,请参阅更新后的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-10
    • 1970-01-01
    • 1970-01-01
    • 2016-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多