2017-09-04 109 views
0

讓我們有以下數據相當於甲骨文NTH_VALUE功能

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) 

它返回正確的結果,但它在我的服務器上花費超過7s的CPU時間。此外,該解決方案的時間隨着我需要的NTH個數值線性增長。是否有更好的(更快,也許更優雅)的方式來計算SQL Server中的NTH_VALUE?

回答

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); 
+0

非常好的一個。需要少於2s的CPU時間才能執行。 –