Oracle SQL - 获取具有 NULL 或最大值的行

Oracle SQL - get the row with either NULL or the max value

我想要一个 return 总是一行的 where 子句。
如果对于 return 多行的查询,特定(可为空的 DateTime)字段中的值之一为 NULL,则它应该 return 此行。
如果没有值为 NULL 的行,它应该 return 具有最大日期时间的行。

例如:

Id Date
1 2022-01-01
2 NULL
3 2021-01-01

在此示例中,ID=2 的行应 returned。

Id Date
1 2022-01-01
2 2020-01-01
3 2021-01-01

在没有 NULL 行的示例中,ID=1 行应该 returned(因为它具有最高日期)。

这怎么可能?

提前致谢

我们可以在这里使用 ROW_NUMBER 和 two-tier 排序逻辑:

WITH cte AS (
    SELECT t.*, ROW_NUMBER() OVER (
                    ORDER BY CASE WHEN Date IS NULL THEN 0 ELSE 1 END,
                    Date DESC) rn
    FROM yourTable t
)

SELECT Id, Date
FROM cte
WHERE rn = 1;

第一个排序级别将 NULL 日期记录放在非 NULL 日期记录之前。第二种排序级别,在没有 NULL 日期记录的情况下使用,首先对较晚的日期进行排序。

如果您有 Oracle 12c 或更高版本:

Select *
From yourtable
Order by coalesce(date, to_date('9999-12-31', 'yyyy-mm-dd')) desc
Fetch first 1 rows only

感谢@littlefoot 和@tim_biegeleisen 指出 TOP n 语法在 Oracle 中不可用;我了解了 FETCH FIRST...!

我将这两个选项都放入示例数据中;它们在 VAR 列的值上有所不同。

SQL> with test (var, id, datum) as
  2    (select 'A', 1, date '2022-01-01' from dual union all
  3     select 'A', 2, NULL              from dual union all
  4     select 'A', 3, date '2021-01-01' from dual union all
  5     --
  6     select 'B', 1, date '2022-01-01' from dual union all
  7     select 'B', 2, date '2020-01-01' from dual union all
  8     select 'B', 3, date '2021-01-01' from dual
  9    ),

CTE排名行;它们按 VAR 分区并按 datum 列的值排序,按降序排序,NULL 值在前:

 10  temp as
 11    (select var, id, datum,
 12            rank() over (partition by var order by datum desc nulls first) rnk
 13     from test
 14    )

最终查询仅 returns 排名最高的行:

 15  select var, id, datum
 16  from temp
 17  where rnk = 1;

V         ID DATUM
- ---------- ----------
A          2
B          1 01.01.2022

SQL>

很快:

with temp as
  (select var, id, datum,
          rank() over (partition by var order by datum desc nulls first) rnk
   from test
  )
select var, id, datum
from temp
where rnk = 1;  
      

您需要最高日期的 ID,其中 null 被认为高于任何实际日期。如果出现平局,您需要最小 ID。您可以为此使用 Oracle 的 KEEP LAST

select min(id) keep (dense_rank last order by date nulls last) from mytable;