是否存在可以在组内返回第一个非空值的聚合函数?

时间:2011-10-17 03:55:23

标签: sql database oracle plsql oracle10g

我正在使用Oracle XE 10g。

请求我仔细阅读我的问题。我有一个奇怪的用例,但请耐心等待。

假设我有以下记录:

Table person
Name  YearOfBirth
a     null
a     2001
a     2002
b     1990
b     null
c     null
c     2001
c     2009

基本上,如果我执行以下查询:

select
  p.Name, max(p.YearOfBirth)
from
  person p
group by
  p.Name

这将为我提供具有不同名称的记录,并且每个不同的名称将与其组内的YearOfBirth的最大值配对。在给定的示例中,Name ='a'的组,最大YearOfBirth是2002。

如果max()是一个返回给定组中列的最大值的聚合函数,是否有一个函数返回组中的第一个值非空? 我希望你能找到的第一个值,而不是给我最大值。

请不要问我为什么我不能简单地使用min()或max()代替。

显然我不能在这里使用rownum,因为这样做会限制我可以获得的群组数量。

3 个答案:

答案 0 :(得分:10)

我可能会误解为什么ROW NUMBER不适合你。我没有Oracle,但我在SQL Server中对此进行了测试,我相信它提供了您请求的结果:

WITH soTable AS
(
   SELECT 'a' AS Name, null AS YearOfBirth
   UNION ALL SELECT 'a', 2001
   UNION ALL SELECT 'a', 2002
   UNION ALL SELECT 'b', 1990
   UNION ALL SELECT 'b', null
   UNION ALL SELECT 'b', 1994
   UNION ALL SELECT 'b', 1981
   UNION ALL SELECT 'c', null
   UNION ALL SELECT 'c', 2009
   UNION ALL SELECT 'c', 2001
)
, soTableNoNulls AS
(
   SELECT so.Name, so.YearOfBirth, ROW_NUMBER() OVER (PARTITION BY so.Name ORDER BY so.Name ASC) AS RowNumber
   FROM soTable AS so
   WHERE so.YearOfBirth IS NOT NULL
)
SELECT nn.Name, nn.YearOfBirth
FROM soTableNoNulls AS nn
WHERE nn.RowNumber = 1

答案 1 :(得分:1)

如果通过" first"你的意思是出生年份最低的记录,那么你可以做到以下几点:

WITH s1 AS
(
   SELECT 'a' AS name, NULL AS birth_year FROM dual
   UNION ALL SELECT 'a', 2001 FROM dual
   UNION ALL SELECT 'a', 2002 FROM dual
   UNION ALL SELECT 'b', 1990 FROM dual
   UNION ALL SELECT 'b', null FROM dual
   UNION ALL SELECT 'b', 1994 FROM dual
   UNION ALL SELECT 'b', 1981 FROM dual
   UNION ALL SELECT 'c', null FROM dual
   UNION ALL SELECT 'c', 2009 FROM dual
   UNION ALL SELECT 'c', 2001 FROM dual
)
SELECT name, birth_year FROM (
    SELECT name, birth_year
         , FIRST_VALUE(birth_year IGNORE NULLS) OVER ( PARTITION BY name ORDER BY birth_year ) AS first_birth_year
      FROM s1
) WHERE birth_year = first_birth_year

使用FIRST_VALUE()优于ROW_NUMBER()的优势在于,前者在发生关系时会返回多行。例如,如果您的数据中有2001年出生的另一个a,那么结果数据将如下所示:

NAME  BIRTH_YEAR
a     2001
a     2001
b     1981
c     2001

ROW_NUMBER()解决方案只会返回上述行中的一行。但是,这也可以通过使用RANK()来解决。

如果有其他方式来定义"首先" (例如,输入日期列),只需在ORDER BY的{​​{1}}子句中使用它。

答案 2 :(得分:0)

这是解决方案:

CREATE OR REPLACE FUNCTION first_agg ( anyelement, anyelement )
RETURNS anyelement AS
$$
    SELECT $1;
$$
LANGUAGE SQL
IMMUTABLE
;

然后:

CREATE AGGREGATE first (
        sfunc    = first_agg,
        basetype = anyelement,
        stype    = anyelement
);

测试:

select first((case when a = 1 then null else a end) ORDER BY a NULLS FIRST) from generate_series(1, 100) a; -- => "2"