SQL - 在RANK()或ROW_NUMBER()之后返回第一个非NULL值

时间:2015-07-16 19:42:53

标签: sql sql-server sql-server-2008 join row-number

以下问题:

我有一个表(客户端),它拥有多个具有相同用户名的记录(不幸的是,它是软件前端的限制)。当我在用户名上加入该表时,它显然会返回多行。通常我使用ROW_NUMBER(),按照我想要的字段上的用户名和顺序进行分区,然后加入以将结果集限制为1并返回所需的列,这在过去一直运行良好。但是,我试图以前所未有的方式使用它...我按用户名进行分区,并按记录最后更新的日期排序,以便返回第三个字段,在本例中为地址。观察下表结果:

Username|UpdatedDate|Address
JSmith  |10-10-14   |NULL
JSmith  |05-24-14   |1 Main Street

对象是返回最新记录,但如果最新记录不包含地址,则从上一记录中获取地址。

这可能吗?

编辑:

@Gordon ......也许我误解了一些东西,但在第一种方法中,订购时不考虑最后更新的日期。如果表格如下:

JSmith|10-10-14|1 Main
JSmith|04-20-14|1 Main 
JSmith|01-10-13|5 Main

它将为所有行返回1。最终目标是,如果只有一条记录,那就抓住那条记录。如果有多条记录,要获取最新记录,但如果地址为NULL,请使用地址获取下一个最新记录。

2 个答案:

答案 0 :(得分:1)

只需使用正确的row_number()逻辑:

select c.*
from (select c.*,
             row_number() over (partition by username
                                order by (case when address is not null then 1 else 2 end), UpdatedDate desc
                               ) as seqnum
      from clients c
     ) c
where seqnum = 1;

当然,如果您不介意过滤掉没有地址的username,您可以使用:

select c.*
from (select c.*,
             row_number() over (partition by username
                                order by UpdatedDate desc
                               ) as seqnum
      from clients c
      where address is not null
     ) c
where seqnum = 1;

但你最终可能会失去一些记录。

答案 1 :(得分:0)

除了row_number()之外,还使用apply运算符定位最近的地址,以便获得最新数据的整行以及最新的地址,即使在最新的地址恰好为空的情况下也是如此行。

SELECT
    c.*
FROM (
    SELECT
        c.*
      , ROW_NUMBER() OVER (PARTITION BY username
                           ORDER BY UpdatedDate DESC) AS seqnum
      , COALESCE(c.address, oa.lastaddress) AS lastaddress
    FROM clients c
    OUTER APPLY (
        SELECT TOP (1)
            address AS lastaddress
        FROM clients c2
        WHERE c.username = c2.username
        AND c2.adress IS NOT NULL
        ORDER BY
            UpdatedDate DESC
    ) oa
) c
WHERE seqnum = 1;