【问题标题】:T-SQL Query to return the closest valuesT-SQL 查询以返回最接近的值
【发布时间】:2018-03-18 07:40:43
【问题描述】:

我在 SQL Server 表中有一些数据,如下所示:

DateTime                Bid       Ask 
02/10/2017 09:59.323    123.111   123.894 
02/10/2017 10:01.432    123.321   124.001  
02/10/2017 10:03.132    123.421   124.121
02/10/2017 10:03.983    123.121   123.721 
02/10/2017 10:04.342    123.587   124.200

我想查询的是每隔一个时间段的出价和要价值是多少。例如在:

  • 10:00.000 买入价为 123.111,卖出价为 123.894
  • 10:01.000 买入价为 123.111,卖出价为 123.894
  • 10:02.000 买入价为 123.321,卖出价为 124.001
  • 10:03.000 买入价为 123.321,卖出价为 124.001
  • 10:04.000 买入价为 123.121,卖出价为 123.721

因此,SQL 需要在每一秒值之前返回 Date Time 的 Bid 和 Ask 值。

例如:

  • 10:00.000 使用 09:59.323
  • 10:01.000 使用 09:59.323
  • 10:02.000 使用 10:01.432
  • 10:03.000 使用 10:01.432
  • 10:04.000 使用 10:03.983

我的查询将返回开始和结束日期/时间之间的值,因此它将返回多行。

【问题讨论】:

  • 您是否有要使用的第二个日期/时间值的表格?还是生成该列表是问题的一部分?
  • 我没有存储第二个值的表(例如 10:00.000、10:01.000、10:02.000 等),因为可能存储了数年的日期。所以我认为应该生成列表。但我会知道显示值之间的日期/时间
  • 我们如何知道要在列表中使用什么范围?而10:01.000 并不是一个很好的时间参考。它有小时 (10)、分钟 (:01) 和毫秒 (.000)。它完全跳过秒部分,并且 Sql Server 为您帖子中的每个时间戳使用隐含的 0 值作为秒数。你确定不想要这个:10:00:01.000

标签: sql sql-server tsql


【解决方案1】:

你可以试试这个查询:

if object_id('tempdb..#Table1') is not null
  drop table #Table1
go
create table #Table1(
DateTime datetime
,Bid float     
,Ask float)

insert into #Table1
select '02/10/2017 09:59.323', 123.111, 123.894 
union all select '02/10/2017 10:01.432', 123.321, 124.001  
union all select '02/10/2017 10:03.132', 123.421, 124.121
union all select '02/10/2017 10:03.983', 123.121, 123.721 
union all select '02/10/2017 10:04.342', 123.587, 124.200

declare @start_date datetime
      , @end_date datetime

select @start_date = dateadd(mi, datediff(mi, 0, min(DateTime)) + 1, 0)
  from #Table1

select @end_date = dateadd(mi, datediff(mi, 0, max(DateTime)), 0) 
  from #Table1

;with generates_dates as(
select @start_date as dt
union all
select dateadd(mi, 1, dt) as dt
  from generates_dates
  where dt < @end_date)

select t1.dt
     , t2.Bid
     , t2.Ask
  from generates_dates t1
    cross apply(select top 1 Bid, Ask 
                  from #Table1 t2 
                  where t2.DateTime < t1.dt 
                  order by t2.DateTime desc)t2(Bid, Ask)
  option (maxrecursion 0)

【讨论】:

    【解决方案2】:

    按日期时间降序排列表格,将返回值限制为 1,然后确保返回的任何值都低于提供的日期时间。

    [编辑]

    select top 1 bid, ask
    from datatable
    where itsdatetime <= '02/10/2017 10:00.000'
    order by itsdatetime desc
    

    您可以获得更多创意并将其放入条件子查询中。

    select *
    from requiretimes rt
    join datatable dt on dt.itsdatetime = (select top 1 itsdatetime
                        from datatable
                        where itsdatetime <= rt.requireddatetime
                        order by itsdatetime desc)
    

    【讨论】:

    • 你能举个例子吗?谢谢
    【解决方案3】:
    DECLARE @T TABLE 
    (
        d   DateTime, 
        bid MONEY,
        ask MONEY
    )
    INSERT INTO @T VALUES
    ('02/10/2017 09:59.323',    123.111,   123.894),
    ('02/10/2017 10:01.432',    123.321,   124.001),  
    ('02/10/2017 10:03.132',    123.421,   124.121),
    ('02/10/2017 10:03.983',    123.121,   123.721), 
    ('02/10/2017 10:04.342',    123.587,   124.200),
    ('03/10/2017 10:04.342',    123.587,   124.200)
    
    ;WITH sec AS 
    (
        SELECT TOP (SELECT 60*60*24) ROW_NUMBER() OVER (ORDER BY 1/0) as s
        FROM master..spt_values a,master..spt_values m
    ), dd as
    (
        SELECT DISTINCT CAST(d as date ) as d
        FROM @t
    ), Tbl as 
    (
        SELECT
            DATEADD(ss,b.s,CAST(a.d as datetime)) as dat
        FROM     
            dd  a
        CROSS JOIN 
            sec b   
    )
    SELECT
        dat 
        ,c.*
    FROM     tbl
    CROSS APPLY 
    (
        SELECT TOP 1 *
        FROM @t a
        WHERE 
            a.d >= tbl.dat
        ORDER BY 
            a.d ASC
    ) as c
    WHERE 
        c.d >= dat  AND 
    ORDER BY dat
    

    【讨论】:

      【解决方案4】:

      这有两个部分:

      1. 创建一个 projection 保存您的时间戳:10:00.000、10:01.000、10:02.000 等。这很难在此处的答案中显示,因为我们不知道是什么您用于确定开始和结束范围的标准,并且因为您的问题涉及秒数,但您的时间戳值实际上显示的是 分钟。如果您需要这方面的帮助,可以在 Google 和 Stack Overflow 上找到很多用于创建投影、数字表或序列的结果。

      2. 使用OUTER APPLY 运算符将投影连接回原始数据。 OUTER APPLY 可以轻松地为您的投影中的每个项目准确显示一个正确的记录。

      .

      WITH times As (
          SELECT cast('2017-02-10 10:00.000' as datetime) "DateTime"
          UNION
          SELECT cast('2017-02-10 10:01.00' as datetime)
          UNION
          SELECT cast('2017-02-10 10:02.000' as datetime) 
          UNION
          SELECT cast('2017-02-10 10:03.000' as datetime)
          UNION
          SELECT cast('2017-02-10 10:04.000' as datetime)
          UNION
          SELECT cast('2017-02-10 10:05.000' as datetime)
      )
      SELECT t.[DateTime], u.[DateTime], u.Bid, u.Ask
      FROM times t
      CROSS APPLY ( 
            SELECT TOP 1 * 
            FROM [MyTable] 
            WHERE [DateTime] < t.[DateTime] 
            ORDER BY [DateTime] DESC
      ) u
      ORDER BY t.[DateTime]
      

      SQLFiddle

      【讨论】:

        【解决方案5】:

        您将使用递归查询生成秒数。 (由于 SQL Server 不支持 ANSI 时间戳文字,因此您需要 CONVERT。)然后通过 CROSS APPLY 加入以每秒获取表中的最后一个条目。

        with secs(sec) as
        (
          select convert(datetime, '2017-10-02 10:00:00', 20) as sec
          union all
          select dateadd(second, 1, sec) as sec 
          from secs
          where sec <= convert(datetime, '2017-10-02 10:00:04', 20)
        )
        select secs.sec, data.bid, data.ask
        from secs
        cross apply
        (
          select top(1) *
          from mytable
          where mytable.datetime <= secs.sec
          order by datetime desc
        ) data;
        

        根据您的描述,我在这里使用秒数,而您的示例使用分钟数。确定你真正需要的。

        【讨论】:

          【解决方案6】:

          试试这个:

          declare @idate datetime
          declare @fdate datetime
          
          select @idate = min(gendate) from BidAsk
          select @fdate = max(gendate) from BidAsk
          
          create table #temp (bid float, ask float, Gendate datetime)
          
          while (@idate <= @fdate)
          begin
             insert into #temp
             select top 1 Bid, Ask, @idate from BidAsk where @idate > GenDate
             order by GenDate desc
          
             set @idate = DATEADD(second,1,@idate)
          end
          
          select * from #temp
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-08-06
            • 1970-01-01
            • 2016-08-27
            • 1970-01-01
            相关资源
            最近更新 更多