Sql server 从SQL数据库中提取(采样)时间序列

Sql server 从SQL数据库中提取(采样)时间序列,sql-server,tsql,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

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

您应该能够使用DATENAME获取某一天的所有记录:

SELECT *
FROM table
WHERE DATENAME(WEEKDAY, date) = 'Friday'
这会导致在查询计划中进行扫描,因此建议使用另一列中的星期几,您可以选择dayOfWeekCol='Friday'

您可以使用交叉联接或内部联接。我个人会选择内部连接,因为它效率更高

样本数据:

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)
使用交叉联接进行查询:

;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;
结果:

使用内部联接进行查询:

;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;
结果:


此解决方案支持从第一个值时间起最多252周的时间窗口

缺少所需输出的第一行,因为星期五在第一个值之前。 如果需要,您可以通过表的最小值的并集来添加它

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

我找到了自己的解决方案,我觉得更具可读性。我首先使用WHILE循环来生成我要查找的日期。然后,我使用外部应用程序将这些日期“连接”到实际数据表中,该应用程序查找“特定日期之前的最后一个值”。代码如下:

-- 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)

谢谢大家的想法和支持

不幸的是,这并不奏效。我需要选择比数据库中存在的条目更多的条目。具体地说,我认为我需要首先提出一个日期向量,在这个向量中我想要提取最新的数据。嗯,交叉连接在这里确实有效,但在我的用例中,采样率实际上明显高于数据率,大约1:10。。因此,我必须将日期移动10倍,以避免出现间隔。我想我将使用“WHILE”生成“输出中所需的日期”-向量,这似乎很容易。@Efrain将内部连接用于您。尽量避免循环,因为它们会影响性能。。这个联合+交叉连接的功能是什么?恐怕我真的听不懂。交叉连接部分子查询A、B和C生成一个记录集,其中包含252个从1到252的数字。MD子查询找出第一个和最后一个日期。您的数据集的所有元素都与所有星期五相关联,从第一个日期之前的日期(8)到最后一个日期13后两周的日期。您的数据从此行开始:03.01.2016 11您的输出包含此行:01.01.2016 11//friday本周五的日期在第一个样本之前。UNION提示只是强制显示该行;在我的解决方案中,它没有显示出来。好吧,这似乎工作得相当漂亮。不幸的是,我想要的输出向量超过252行。我的要求是“自2000年1月1日以来的所有数据”,这给了我大约900行,这也需要在下一个版本中使用。。10年。你认为把这种结合的魔力扩展到那么多的日子是可行的吗?是的,当然。修改A、B和C子查询就足够了。从选择1 S联合选择2联合选择3联合选择4联合选择5联合选择6联合选择7联合选择8联合选择9联合选择10交叉连接选择0 P联合选择10联合选择20联合选择30联合选择40联合选择50联合选择60联合选择70联合选择80联合选择90 B交叉连接选择0 Q UNION SELECT 100 UNION SELECT 200 UNION SELECT 300 UNION SELECT 400 UNION SELECT 500 UNION SELECT 600 UNION SELECT 700 UNION SELECT 800 UNION SELECT 900 C
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