I've got some data in a SQL Server table as followings:
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
What I'd like to query is what the Bid and Ask values were at each second time period. For example at:
So the SQL needs to return the Bid and Ask values for the Date Time before each second value.
For example for:
And my query will return values between a start and end date/time, so it'll return multiple rows.
Order the table by datetime decending, limit the return to 1 and then make sure that any values returned are lower than the supplied datetime.
[edit]
select top 1 bid, ask
from datatable
where itsdatetime <= '02/10/2017 10:00.000'
order by itsdatetime desc
You can get more creative and put that inside of condition subquery.
select *
from requiretimes rt
join datatable dt on dt.itsdatetime = (select top 1 itsdatetime
from datatable
where itsdatetime <= rt.requireddatetime
order by itsdatetime desc)
You can try this query:
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)
There are two parts to this:
Create a projection holding your timestamps: 10:00.000, 10:01.000, 10:02.000 etc. This is hard to show in an answer here, because we don't know what criteria you're using to determine your start and end ranges, and because your question asks about seconds, but your timestamp values are actually showing minutes . If you need help with this, there are lots of results in Google and here on Stack Overflow for creating projections, number tables, or sequences.
Use the OUTER APPLY
operator to connect the projection back to your original data. OUTER APPLY
makes it easy to show exactly the one right record for each item from your projection.
.
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]
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
You'd generate the seconds with a recursive query. (As SQL Server doesn't support ANSI timestamp literals, you'll need CONVERT
for this.) Then join via CROSS APPLY
to get the last entry from the table per second.
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;
I am using seconds here as per your description, while your sample uses minutes instead. Decide which you actually need.
Try this:
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.