简体   繁体   中英

SQL start time end time query

so I have table like this

CREATE TABLE Table_Status
(
Status VARCHAR(10) NOT NULL,
StartTime DATETIME NOT NULL, 
EndTime DATETIME NOT NULL
); 

and data looks like this, StartTime and Endtime are consecutive time span :

Status1 2007-10-16 18:38:25.000 2007-10-17 05:30:22.000 
Status2 2007-10-17 05:30:22.000 2007-10-17 18:48:46.000
Status2 2007-10-17 18:48:46.000 2007-10-17 21:48:46.000
Status1 2007-10-17 21:48:46.000 2007-10-18 08:11:59.000

So the idea is to SELECT * within any time period , if user pass two parameters

SET @From = '2007-10-17 00:00:00.000'
SET @To = '2007-10-17 23:59:59.000' 

Somehow it should return table like this:

Status1 2007-10-17 00:00:00.000 2007-10-17 05:30:22.000
Status2 2007-10-17 05:30:22.000 2007-10-17 21:48:46.000
Status1 2007-10-17 21:48:46.000 2007-10-17 23:59:59.000

You see, the tricky part is to cut the original timespan to user defined timespan(@From - @To), I have been struggling with this whole day. Please advise.

Thank you so much in advance!!!

There are two parts to get the result set you are looking for.

  1. Merging all contiguous dates with the same "Status" value together
  2. Returning a combined resultset that has:

    • All ranges that fall completely within your date from/to parameters
    • A calculated range that partially overlaps with the beginning of your from/to parameters
    • A calculated range that partially overlaps with the end of your from/to parameters

For the date merging you can look at these two links to get ideas of ways to merge contiguous date ranges:

http://www.sqlservercentral.com/Forums/Topic1364849-392-1.aspx

http://sqlmag.com/blog/solutions-packing-date-and-time-intervals-puzzle

I have used one of the examples there that was the most readable. However you could look through others if you are looking for queries that are more performant.

Here is an example query that returns the resultset in your example:

Merge contiguous date rows with the same status

with all_times (time_type,date_range_part,status) as (
select 'start',
       starttime,
       status
from table_status
union all
select 'end',
       endtime,      
       status
from table_status),

ordered_starts as (
select date_range_part,
       status,
       row_number() over(partition by status order by date_range_part, time_type desc) as rnboth,
       2*(row_number() over(partition by status,time_type order by date_range_part))-1 as rnstartend
from all_times),

ordered_ends as (
select date_range_part,
       status,
       row_number() over(partition by status order by date_range_part desc,time_type) as rnbothrev,
       2*(row_number() over(partition by status,time_type order by date_range_part desc))-1 as rnstartendrev
from all_times),

starts as (
select date_range_part,
       status,
       row_number() over(partition by status order by date_range_part) as rn
from ordered_starts
where rnboth=rnstartend),

ends as (
select date_range_part,
       status,
       row_number() over(partition by status order by date_range_part) as rn
from ordered_ends
where rnbothrev=rnstartendrev)

select 
s.status,
s.date_range_part [start_time],
e.date_range_part [end_time]
into #table_status_merged
from starts s
inner join ends e on e.status=s.status and e.rn=s.rn and s.date_range_part<=e.date_range_part
order by s.date_range_part;

Return a resultset that has all ranges completely within your date parameters, a calculated start range, and a calculated end range

declare @from datetime
declare @to datetime

set @from = '2007-10-17 00:00:00.000'
set @to = '2007-10-17 23:59:59.000'

select
[status],
@from,
end_time
from #table_status_merged
where start_time < @from
and end_time <= @to
union all
select
[status],
start_time,
end_time
from #table_status_merged
where start_time >= @from
and end_time <= @to
union all
select
[status],
start_time,
@to
from #table_status_merged
where start_time >= @from
and end_time > @to

drop table #table_status_merged

As mentioned in comments before: As your dates are already consecutive, you can simply UNION together the possible locations of from and to in regards to you start and end-times.

SET @From = '2007-10-17 00:00:00'
SET @To = '2007-10-17 23:59:59'

-- Intervals where from is included, but to is not
SELECT 'A' Union_Case, Status, @From StartTime, EndTime FROM Table_Status 
    WHERE StartTime < @From AND @From <= EndTime AND EndTime < @To
  UNION 
-- Intervals where @to and @from are incased in the interval
SELECT 'B', Status, @From, @To FROM Table_Status WHERE StartTime < @From AND EndTime >= @To
  UNION  
-- Intervals where @from is before start and @to is after end
SELECT 'C', Status, StartTime, EndTime FROM Table_Status WHERE StartTime > @From AND StartTime <= @To AND EndTime < @To 
  UNION 
-- intervals where @from is before start but @to ends within
SELECT 'D',  Status, StartTime, @To FROM Table_Status WHERE @From <= StartTime AND EndTime > @To
  ORDER BY 3, 4

I faced a similar situation, and the way I was able to fix it was this:

 DECLARE @TimeStamp as DateTimeType SET @TimeStamp = cast('2016-10-13 00:00:00.000' as datetime) DECLARE @DayNum as ApsDayOrdinalType = DATEPART(dw,@TimeStamp) DECLARE @Time as Time = cast(@TimeStamp as time) DECLARE @DefaultTime as Time = CAST('00:00' as time) SELECT @DayNum,@Time,@DefaultTime -- CTEs don't work pretty well with inline data transformation :'( DECLARE @WorkCenterShifts as table( WorkCenter varchar(15), ShiftId varchar(15), StartDateTime DateTime, EndDateTime DateTime ) INSERT INTO @WorkCenterShifts SELECT wc ,shiftid ,CAST(CAST(DateAdd(dd,-(@DayNum - sday),@TimeStamp) as date) as varchar) +' '+ cast(stime as varchar) ,CAST(CAST(DateAdd(dd,(@DayNum - eday),@TimeStamp) as date) as varchar) +' '+ cast(etime as varchar) FROM workcentershiftview WHERE wc = @wc and @DayNum >= IsNull(sday,1) and @DayNum <= IsNull(eday,7) 

Where " workcentershiftview " returns data as this:

WC      ShiftId sDay    eDay    sTime   eTime
MXMCE2  2nd9    6       2       16:00   06:30
MXMCE2  1st9.5  6       6       06:30   16:00
MXMCE2  2nd9    5       6       16:00   06:30
MXMCE2  1st9.5  5       5       06:30   16:00
MXMCE2  2nd9    4       5       16:00   06:30

From there, I could finally create a simple query, like this:

 SELECT * FROM @WorkCenterShifts WHERE @TimeStamp between StartDateTime and EndDateTime 

And get a result like this:

 WorkCencer ShiftId StartDateTime EndDateTime MXMCE2 2nd9 2016-10-12 16:00:00.000 2016-10-13 06:30:00.000 

Hope this is useful for others :)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM