我有一个MS SQL数据库,其中包含与其时间戳一起存储的值。所以我的结果表看起来像这样:
date value
03.01.2016 11
19.01.2016 22
29.01.2016 33
17.02.2016 44
01.03.2016 55
06.03.2016 66
时间戳并非真正遵循模式的大部分内容。现在,我需要从中提取每周数据:(例如,周五抽样)
date value
01.01.2016 11 // friday
08.01.2016 11 // next friday
15.01.2016 11
22.01.2016 22
29.01.2016 33
05.02.2016 33
12.02.2016 33
19.02.2016 44
26.02.2016 44
04.03.2016 55
11.03.2016 66
有没有合理的方法直接在T-SQL中执行此操作?
我可以使用C#或Matlab程序重新格式化结果表,但它似乎有点奇怪,因为我似乎再次查询结果表......
答案 0 :(得分:1)
您可以使用CROSS JOIN
或INNER JOIN
。我个人认为INNER JOIN
效率更高。
示例数据:
CREATE TABLE #Temp(SomeDate DATE
, SomeValue VARCHAR(10));
INSERT INTO #Temp(SomeDate
, SomeValue)
VALUES
('20160103'
, 11),
('20160119'
, 22),
('20160129'
, 33),
('20160217'
, 44),
('20160301'
, 55),
('20160306'
, 66)
使用CROSS JOIN进行查询:
;WITH T
AS (SELECT *
FROM #Temp),
D
AS (
SELECT SomeDate
, SomeValue
FROM #Temp AS A
UNION
SELECT DATEADD(day, 7, SomeDate)
, SomeValue
FROM #Temp AS B
UNION
SELECT DATEADD(day, 14, SomeDate)
, SomeValue
FROM #Temp AS C)
SELECT D.*
FROM T
CROSS JOIN D
WHERE T.SomeValue = D.SomeValue
ORDER BY SomeValue
, SomeDate;
结果:
使用INNER JOIN进行查询:
;WITH T
AS (SELECT *
FROM #Temp),
D
AS (
SELECT SomeDate
, SomeValue
FROM #Temp AS A
UNION
SELECT DATEADD(day, 7, SomeDate)
, SomeValue
FROM #Temp AS B
UNION
SELECT DATEADD(day, 14, SomeDate)
, SomeValue
FROM #Temp AS C)
SELECT D.*
FROM T
INNER JOIN D
ON T.SomeValue = D.SomeValue
ORDER BY SomeValue
, SomeDate;
结果:
答案 1 :(得分:1)
此解决方案支持从第一个值时间开始的最长时间窗口为252周。
缺少所需输出的第一行,因为星期五在第一个值之前。 如果需要,您可以通过UNION添加它,并使用表格的最小值。
DECLARE @tbl TABLE ( [date] date, [value] int )
INSERT INTO @tbl
VALUES
('2016-01-03','11'),
('2016-01-19','22'),
('2016-01-29','33'),
('2016-02-17','44'),
('2016-03-01','55'),
('2016-03-06','66')
;WITH DATA
AS (
SELECT (S+P+Q) WeekNum, DATEADD( week, S + P + Q, MinDate ) Fridays, SubFri, [value]
FROM ( SELECT 1 S UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 ) A
CROSS JOIN ( SELECT 0 P UNION SELECT 7 UNION SELECT 14 UNION SELECT 21 UNION SELECT 28 UNION SELECT 35 ) B
CROSS JOIN ( SELECT 0 Q UNION SELECT 42 UNION SELECT 84 UNION SELECT 126 UNION SELECT 168 UNION SELECT 210 ) C
CROSS JOIN (
SELECT
min ( DATEADD( day, -8 - DATEPART(weekday,[date]), [date] ) ) MinDate,
max ( DATEADD( day, 13 - DATEPART(weekday,[date]), [date] ) ) MaxDate
FROM @tbl
) MD
LEFT JOIN ( SELECT DATEADD( day, 6 - DATEPART(weekday,[date]), [date] ) SubFri, [value] FROM @tbl ) Val
ON SubFri<=DATEADD( week, S + P + Q, MinDate )
WHERE DATEADD( week, S + P + Q, MinDate )<=MaxDate
)
SELECT DATA.Fridays, DATA.value
FROM DATA
INNER JOIN
(
SELECT Fridays, max(SubFri) MaxSubFri
FROM DATA
GROUP BY Fridays
) idx
ON DATA.Fridays=idx.Fridays
AND SubFri=MaxSubFri
ORDER BY Fridays
答案 2 :(得分:0)
您应该可以使用DATENAME
获取某一天的所有记录:
SELECT *
FROM table
WHERE DATENAME(WEEKDAY, date) = 'Friday'
这会导致在查询计划中进行扫描,因此建议使用另一个列来查看星期几,您可以选择WHERE dayOfWeekCol = 'Friday'
答案 3 :(得分:0)
我找到了自己的解决方案,我发现它更具可读性。我首先使用WHILE循环生成我正在寻找的日期。那我就加入&#39;这些日期使用OUTER APPLY标记到实际数据表中,该应用程序在特定日期之前查找最后一个值&#39;。这是代码:
-- prepare in-memory table
declare @tbl table ( [date] date, [value] int )
insert into @tbl
values
('2016-01-03','11'),
('2016-01-19','22'),
('2016-01-29','33'),
('2016-02-17','44'),
('2016-03-01','55'),
('2016-03-06','66')
-- query
declare @startDate date='2016-01-01';
declare @endDate date='2016-03-31';
with Fridays as (
select @startDate as fridayDate
union all
select dateadd(day,7,fridayDate) from Fridays where dateadd(day,7,fridayDate)<=@endDate
)
select *
from
Fridays f
outer apply (
select top(1) * from @tbl t
where f.fridayDate >= t.[date]
order by t.[value] desc
) as result
option (maxrecursion 10000)
给我:
fridayDate date value
---------- ---------- -----------
2016-01-01 NULL NULL
2016-01-08 2016-01-03 11
2016-01-15 2016-01-03 11
2016-01-22 2016-01-19 22
2016-01-29 2016-01-29 33
2016-02-05 2016-01-29 33
2016-02-12 2016-01-29 33
2016-02-19 2016-02-17 44
2016-02-26 2016-02-17 44
2016-03-04 2016-03-01 55
2016-03-11 2016-03-06 66
2016-03-18 2016-03-06 66
2016-03-25 2016-03-06 66
感谢大家的想法和支持!