我有一个表,其中一些值存储了数月和数年。
示例:
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
这不是我想做的事情,因为它只是在明年停止了
答案 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