从SQL DB中提取(采样)时间序列

时间:2016-08-09 13:17:18

标签: sql-server tsql

我有一个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程序重新格式化结果表,但它似乎有点奇怪,因为我似乎再次查询结果表......

4 个答案:

答案 0 :(得分:1)

您可以使用CROSS JOININNER 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;

结果:

enter image description here

使用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;

结果:

enter image description here

答案 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

感谢大家的想法和支持!