相当于Oracle NTH_VALUE函数

时间:2017-09-04 13:48:58

标签: sql sql-server

让我们得到以下数据

CREATE TABLE [dbo].[LogTable] ([DateSent] [datetime] NULL) 
GO
CREATE CLUSTERED INDEX [IX_LogTable_DateSent] ON [dbo].[LogTable] ([DateSent] DESC) 
GO

INSERT INTO [LogTable]
SELECT TOP 500000 NULL, DATEADD(day, ( ABS(CHECKSUM(NEWID())) % 65530 ), 0)
FROM sys.sysobjects
CROSS JOIN sys.all_columns

我想在DateSent找到每年的第二个和第三个最低值。 Oracle为此提供了NTH_VALUE函数,但是,SQL Server中没有这样的功能。我创建了以下查询

SELECT YEAR(datesent),   
(
    SELECT datesent
    FROM 
    (
        SELECT datesent, ROW_NUMBER() OVER (ORDER BY datesent) r
        FROM logtable
        WHERE YEAR(datesent) = YEAR(lt.datesent)
    ) logtable_ranked
    WHERE logtable_ranked.r = 2
) second_lowest_in_year,
(
    SELECT datesent
    FROM 
    (
        SELECT datesent, ROW_NUMBER() OVER (ORDER BY datesent) r
        FROM logtable
        WHERE YEAR(datesent) = YEAR(lt.datesent)
    ) logtable_ranked
    WHERE logtable_ranked.r = 3
) thirt_lowest_in_year
FROM logtable lt
GROUP BY YEAR(datesent)

返回正确的结果,但我的服务器上的CPU时间超过7秒。此外,该解决方案的时间随着我需要的许多NTH值线性增长。是否有更好(更快,也许更优雅)的方式来计算SQL Server中的NTH_VALUE?

1 个答案:

答案 0 :(得分:2)

使用row_number()和条件聚合:

SELECT YEAR(datesent),
       MAX(CASE WHEN seqnum = 1 THEN datesent END) AS datesent_1,
       MAX(CASE WHEN seqnum = 2 THEN datesent END) AS datesent_2,
       MAX(CASE WHEN seqnum = 3 THEN datesent END) AS datesent_3
FROM (SELECT datesent,
             ROW_NUMBER() OVER (PARTITION BY YEAR(datesent) ORDER BY datesent) AS seqnum
      FROM LogTable lt
     ) lt
GROUP BY YEAR(datesent)
ORDER BY YEAR(datesent);