postgres / oracle:获取组内的最后一个非null值

时间:2018-03-23 14:17:54

标签: sql oracle postgresql

我有数据,我必须按'分组。在每个结果组中,存在具有多个列的行,必须对其进行处理:对于每个这样的给定列,返回非空的,最大的当前值。因此,我必须按照'(gb)整个表格进行分组,然后找到类似max(NUM)'对于每一列(以下表示为NUM)。 max-like函数按时间列排序,下面表示为' time'。换句话说,按' gb'分组,按时间排序' desc nulls last,获取组中的第一项。

对不起,对于那个错综复杂的描述。我希望它清楚。知道怎么写那个sql查询(oracle / postgres)?

例如

CREATE TABLE test (
  gb integer,
  NUM INTEGER,
  time integer
);

--rows=groups, columns=time; so in first row=group data 
--sorted by time are from left to right the middle value 
--in triplet, thus, 2,1,3. Ie. most current non-null value in time is 3.
insert into test VALUES (1,2,1),(1,1,2),(1,3,3);--3
insert into test VALUES (2,1,1),(2,2,2),(2,3,3);--3
insert into test VALUES (3,3,1),(3,2,2),(3,1,3);--1
insert into test VALUES (4,3,1),(4,2,2),(4,null,3);--2
insert into test VALUES (5,2,1),(5,3,2),(5,null,3);--3
insert into test VALUES (6,2,1),(6,null,2),(6,null,3);--2

查询

select
  t.gb,
  '<magic goes here>'
from test t
GROUP BY t.gb ORDER BY t.gb;

预计将返回

1 | 3
2 | 3
3 | 1
4 | 2
5 | 3
6 | 2

4 个答案:

答案 0 :(得分:3)

在Oracle中,最简单的方法是:

SELECT gb, max(num) keep (DENSE_RANK LAST ORDER BY nvl2(num,time,NULL) NULLS first ) r 
FROM test 
GROUP BY gb

SQLfiddle

还有一个&#34;无团体&#34;的方法:

SELECT DISTINCT gb, last_value(num ignore nulls)over(PARTITION BY gb ORDER BY time 
       RANGE BETWEEN UNBOUNDED preceding AND UNBOUNDED following) num
FROM test ORDER BY gb 

SQLfiddle

GB  NUM 
--- ----
1   3   
2   3   
3   1   
4   2   
5   3   
6   2   

答案 1 :(得分:2)

您可以使用row_number为具有相同gb的每一行分配一个递增的数字。按time排序这些行,只显示第一行:

select  gb
,       num
from    (
        select  row_number() over (partition by gb order by time desc) rn
        ,       *
        from    test
        where   num is not null
        ) sub
where   rn = 1  -- Row with highest time per (gb) group

Working example at SQL Fiddle.

答案 2 :(得分:0)

这可能是适用于Oracle和PostgreSQL的最简单的解决方案:

SELECT DISTINCT
   gb,
   first_value(num) OVER (PARTITION BY gb ORDER BY time DESC)
FROM test
WHERE num IS NOT NULL
ORDER BY gb;

答案 3 :(得分:0)

以下是标准SQL中的解决方案:

with grp as
(
  select distinct gb from mytable
)
SELECT 
  gb,
  (
    select num 
    from mytable m 
    where m.gb = grp.gb and m.num is not null 
    order by thetime desc 
    fetch first row only
  ) as n
FROM grp
order by grp.gb\\

每列需要一个这样的子查询。

在Oracle中,此查询仅适用于版本12c;以前的版本没有FETCH FIRST n ROWS子句。

这是另一种标准SQL,它可以在较旧的Oracle版本中运行:

with grp as
(
  select distinct gb from mytable
)
SELECT 
  gb,
  (
    select num 
    from mytable m 
    where m.gb = grp.gb and thetime = 
    (
      select max(thetime) 
      from mytable m2 
      where m2.gb = m.gb and m2.num is not null
    )
  ) as n
FROM grp
order by grp.gb\\

Rextester演示:http://rextester.com/QUO91858