SQL Server - 选择前2行

时间:2011-03-24 14:52:32

标签: sql-server tsql syntax

我正在尝试编写一个将返回的查询

  1. 最近的AccountDate,每个locationID的记录为0
  2. 然后是每个locationID的第二个最近的AccountDate。记录可以是1或0.
  3. 如果有两个具有相同日期的AccountDates,则返回基于DateAccountLoaded的最新AccountDate
  4. 我的解决方案看起来并不优雅。有没有人有更好的方法来实现这一目标。

    请参阅下面的解决方案

    CREATE TABLE [dbo].[TopTwoKeyed](
    ID  INT IDENTITY(1,1) PRIMARY KEY(ID),
    [LocationID] [int] NULL,
    [AccountDate] [date] NULL,
    [Record] [tinyint] NULL,
    [DateAccountLoaded] [date] NULL
    )
    
    INSERT INTO [dbo].[TopTwoKeyed] (
            [LocationID],
            AccountDate,
            Record,
            DateAccountLoaded 
            )
    
    VALUES(1,'2009-10-31',0,'2011-03-23'),
    (1,'2008-10-31',1,'2011-03-23'),
    (1,'2008-10-31',0,'2010-03-22'),
    (1,'2008-10-31',1,'2009-03-23'),
    (1,'2011-10-31',1,'2010-03-22'),
    (1,'2009-10-31',0,'2010-03-23'),
    (2,'2011-10-31',0,'2010-03-23'),
    (2,'2010-10-31',0,'2010-03-23'),
    (2,'2010-10-31',1,'2010-03-23'),
    (2,'2010-10-31',1,'2009-03-23'),
    (3,'2010-10-31',0,'2010-03-23'),
    (3,'2009-10-31',0,'2010-03-23'),
    (3,'2008-10-31',1,'2010-03-23')
    
    
    
    
        -- Get the most recent Account Date per locationID which has a record type of 0
        SELECT  f.LocationID
                ,f.AccountDate
                ,f.DateAccountLoaded
        FROM    (
                SELECT  ROW_NUMBER() OVER (PARTITION BY LocationID ORDER BY AccountDate DESC,DateAccountLoaded DESC) AS RowNumber
                        ,LocationID AS LocationID
                        ,AccountDate AS AccountDate
                        ,DateAccountLoaded AS DateAccountLoaded
                FROM    [dbo].[TopTwoKeyed]
                WHERE   Record = 0
                ) f
        WHERE   f.RowNumber = 1
    
        UNION ALL
    
        SELECT  ff.LocationID
                ,ff.AccountDate
                ,ff.DateAccountLoaded
        FROM    (
                -- Get the SECOND most recent AccountDate. Can be either Record 0 or 1. 
                SELECT  ROW_NUMBER() OVER (PARTITION BY LocationID ORDER BY AccountDate DESC,DateAccountLoaded DESC) AS RowNumber
                        ,LocationID AS LocationID
                        ,AccountDate AS AccountDate
                        ,DateAccountLoaded 'DateAccountLoaded'
                FROM    [dbo].[TopTwoKeyed] tt
                WHERE   EXISTS 
                        (
                        -- Same query as top of UNION. Get the most recent Account Date per locationID which has a record type of 0
                        SELECT  1
                        FROM    (
                                SELECT  ROW_NUMBER() OVER (PARTITION BY LocationID ORDER BY AccountDate DESC,DateAccountLoaded DESC) AS RowNumber
                                        ,LocationID AS LocationID
                                        ,AccountDate AS AccountDate
                                FROM    [dbo].[TopTwoKeyed]
                                WHERE   Record = 0
                                ) f
                        WHERE   f.RowNumber = 1
                        AND     tt.LocationID = f.LocationID 
                        AND     tt.AccountDate < f.AccountDate 
                        )
                ) ff
        WHERE   ff.RowNumber = 1
    
    -- DROP TABLE [dbo].[TopTwoKeyed]
    

1 个答案:

答案 0 :(得分:2)

您可以使用row_number子查询查找最近的帐户日期。然后,您可以outer apply搜索下一个最近的帐户日期:

select  MostRecent.LocationID
,       MostRecent.AccountDate
,       SecondRecent.AccountDate
from    (
        select  row_number() over (partition by LocationID order by 
                    AccountDate desc, DateAccountLoaded desc) as rn
        ,       *
        from    TopTwoKeyed
        where   Record = 0
        ) MostRecent
outer apply
        (
        select  top 1 *
        from    TopTwoKeyed
        where   Record in (0,1)
                and LocationID = MostRecent.LocationID
                and AccountDate < MostRecent.AccountDate
        order by 
                AccountDate desc
        ,       DateAccountLoaded desc
        ) SecondRecent
where   MostRecent.rn = 1

编辑:要将行放在彼此之下,您可能必须使用union。单个row_number无效,因为第二行对Record列的标准不同。

; with  Rec0 as
        (
        select  ROW_NUMBER() over (partition by LocationID 
                    order by AccountDate desc, DateAccountLoaded desc) as rn
        ,       *
        from    TopTwoKeyed
        where   Record = 0
        )
,       Rec01 as
        (
        select  ROW_NUMBER() over (partition by LocationID 
                    order by AccountDate desc, DateAccountLoaded desc) as rn
        ,       *
        from    TopTwoKeyed t1
        where   Record in (0,1)
                and not exists
                (
                select  *
                from    Rec0 t2
                where   t2.rn = 1
                        and t1.LocationID = t2.LocationID
                        and t2.AccountDate < t1.AccountDate
                )
        )
select  *
from    Rec0
where   rn = 1
union all
select  *
from    Rec01
where   rn = 1