选择之前的N行和记录之后的N行

时间:2016-04-19 14:43:05

标签: sql-server tsql sql-server-2012 common-table-expression

我有一个表,其中一些值存储了数月和数年。

示例:

Month  |   Year   |  Value
  1    |   2013   |  1.86
  2    |   2013   |  2.25
  3    |   2013   |  2.31
  ...
  3    |   2016   |  1.55
  4    |   2016   |  1.78

月份和年份组合是一个复杂的主键。保证所有过去几年的所有值都存在于表中。

用户可以选择特定月份和特定年份。让我们说用户选择2014年作为年份,将6作为月份,我需要在所选组合之前显示15行和15行。

但是如果所选组合之后没有足够的行(小于15),那么我之前需要获得更多的行。

基本上我只需要返回31行(除非整个表中没有足够的行),否则所选组合将尽可能接近中心。

这样做的正确方法是什么?

目前我一直坚持这个:

;WITH R(N) AS
(
    SELECT 0
    UNION ALL
    SELECT N+1 
    FROM R
    WHERE N < 29
)
SELECT  * FROM MyTable e
LEFT OUTER JOIN (
    SELECT N, MONTH(DATEADD(MONTH,-N,iif(@year != Year(GETDATE()), DATEFROMPARTS(@year, 12, 31) ,GETDATE()))) AS [Month], 
   YEAR(DATEADD(MONTH,-N,iif(@year!= Year(GETDATE()), DATEFROMPARTS(@year, 12, 31) ,GETDATE()))) AS [Year]
FROM R) s
ON s.[Year] = e.[Year] AND s.[Month] = e.[Month]
WHERE s.[N] is not null

这不是我想做的事情,因为它只是在明年停止了

4 个答案:

答案 0 :(得分:1)

如此简单的事情:

;WITH CTE AS (
    SELECT Month
        ,Year
        ,Value
        ,ROW_NUMBER() OVER (ORDER BY Year, Month) rn
    FROM MyTable
    )
SELECT Month
    ,Year
    ,Value
FROM CTE 
WHERE rn >= (SELECT rn - 15 FROM MyTable WHERE Year = @Year AND Month = @Month)
    AND rn <= (SELECT rn + 15 FROM MyTable WHERE Year = @Year AND Month = @Month);

我确信这是一种更有效的方式,但这让我觉得这是最易于维护的方式。当您选择接近表中第一个或最后一个记录的值时,它甚至应该起作用。

无论如何,我都无法判断你是否需要31行。有一点听起来像你这样做,而另一点听起来就像你没有。

编辑:好的,所以你总是想要31行(如果有的话)。

好的,试试这个:

;WITH CTE AS (
    SELECT Month
        ,Year
        ,Value
        ,ROW_NUMBER() OVER (ORDER BY Year, Month) rn
    FROM MyTable
    ),
CTE_2 AS (
    SELECT TOP (31) Month
        ,Year
        ,Value
    FROM CTE
    ORDER BY ABS(rn - (SELECT rn FROM MyTable WHERE Year = @Year AND Month = @Month)) ASC
    )
SELECT Month
    ,Year
    ,Value
FROM CTE_2
ORDER BY Year, Month;

基本上,你计算与目标行号的差异,得到那里的前31行,然后求它们输出。

答案 1 :(得分:0)

检查一下,

DECLARE @iPrevRows int
DECLARE @iPostRows int
DECLARE @Year int = 2016
DECLARE @Month int = 2

SELECT @iPrevRows= Count(*) 
FROM
[GuestBook].[dbo].[tblTest]
where  (year < @Year ) 
  or (year =@Year and month < @Month)

SELECT @iPostRows= count(*)  from
[GuestBook].[dbo].[tblTest]
where  (year > @Year ) 
  or (year =@Year and month > @Month)

if (@iPrevRows > 15) 
    select @iPrevRows =15

if (@iPostRows > 15) 
    select @iPostRows =15

if (@iPrevRows  < 15 )
   select @iPostRows = @iPostRows  + (15-@iPrevRows)
else if (@iPostRows  < 15 )
   select @iPrevRows = @iPrevRows  + (15-@iPostRows)

CREATE TABLE #tempValues
(
 Year int NOT NULL,
 Month int NOT NULL,
 Value  float
)

insert into #tempValues

SELECT top (@iPrevRows) Month, Year, Value 
from
[GuestBook].[dbo].[tblTest]
where  (year < @Year )  
or (year =@Year and month < @Month)
order by 2 desc,1 desc

insert into #tempValues
SELECT Month, Year, Value   
from
[GuestBook].[dbo].[tblTest]
where     (year =@Year and month = @Month)

insert into #tempValues
SELECT top (@iPostRows) Month, Year, Value   
from
[GuestBook].[dbo].[tblTest]
where  (year > @Year ) 
  or (year =@Year and month > @Month)
  order by 2 ,1 

select * from #tempValues
order by 2,1

答案 2 :(得分:0)

这就是我所做的,似乎在起作用

select * from (
  select top(31) * from MyTable r
  order by ABS(DATEDIFF(month, DATEFROMPARTS(r.Year, r.Month, 1), DATEFROMPARTS(@Year, @Month, 1)))) s
order by Year, Month

答案 3 :(得分:0)

我是这样做的。

DECLARE @year INT = 2014, @month INT = 6;
WITH TableAux
     AS (SELECT MyTable.Month
              , MyTable.Year
         FROM MyTable
         WHERE MyTable.Year = @year
         AND MyTable.Month = @month)
     SELECT tb1.Month
          , tb1.Year
          , tb1.Value
     FROM
     (
         SELECT TOP 16 MyTable.Month
                     , MyTable.Year
                     , MyTable.Value
         FROM MyTable
         CROSS JOIN TableAux
         WHERE MyTable.Month <= TableAux.Month
         AND MyTable.Year <= TableAux.Year
         ORDER BY MyTable.Month DESC, MyTable.Year DESC
     ) tb1
     UNION ALL
     SELECT tb2.Month
          , tb2.Year
          , tb2.Value
     FROM
     (
         SELECT TOP 15 MyTable.Month
                     , MyTable.Year
                     , MyTable.Value
         FROM MyTable
         CROSS JOIN TableAux
         WHERE MyTable.Month > TableAux.Month
         AND MyTable.Year > TableAux.Year
         ORDER BY MyTable.Month, MyTable.Year
     ) tb2
     ORDER BY Year, Month