Create 15 min inc time matrix?

  • tacy.highland (7/18/2012)


    Thank you so much. I think you might have gotten it, looking at what your output is, but when I tried running the code I'm getting an error: "Invalid object name 'Tally' ". Did I miss something?

    yes tally is my tally table. you can either create a tally table or use the cteTally i posted earlier.


    For faster help in answering any problems Please read How to post data/code on a forum to get the best help - Jeff Moden[/url] for the best way to ask your question.

    For performance Issues see how we like them posted here: How to Post Performance Problems - Gail Shaw[/url]

    Need to Split some strings? Jeff Moden's DelimitedSplit8K[/url]
    Jeff Moden's Cross tab and Pivots Part 1[/url]
    Jeff Moden's Cross tab and Pivots Part 2[/url]

  • My apologies, I've not used cte's before but I know something about how they need to be used after they're created. But I'm just not putting that tally table in the right place somehow. Based on your code, this is what i did:

    CREATE TABLE #BusRouteTime (

    BusRoute INT,

    TimeOfDay TIME,

    InService INT

    )

    --and the assumed BusRoutes table

    CREATE TABLE #BusRoutes(

    RouteNumber INT,

    StartTime DATETIME,

    EndTime DATETIME)

    --And some sample date to work with in the BusRoutes Table

    INSERT INTO #BusRoutes VALUES (1234,'2012-07-07 09:32','2012-07-07 10:18')

    INSERT INTO #BusRoutes VALUES (5678,'2012-07-07 09:47','2012-07-07 10:28')

    --Tally table

    ;WITH E1(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 ),

    E2(N) AS (SELECT 1 FROM E1 a, E1 b),

    cteTally(N) AS (SELECT 0 UNION ALL

    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))

    FROM E2)

    --Increments table

    ;WITH TimePart AS (SELECT RouteNumber, DATEPART(hour,StartTime) AS StartHour, DATEPART(MI,StartTime) AS StartMin,

    DATEPART(hour,EndTime)AS EndHour, DATEPART(MI,EndTime) AS EndMin

    FROM #BusRoutes),

    StartEnd AS (SELECT *, CAST(CAST(StartHour AS VARCHAR) + ':' + CAST ( CASE WHEN StartMin % 15 = 0 THEN StartMin

    ELSE (StartMin / 15) * 15 END AS VARCHAR) AS Time) AS StartTime,

    CAST(CAST(EndHour AS VARCHAR) + ':' + CAST ( CASE WHEN EndMin % 15 = 0 THEN EndMin

    ELSE ((EndMin / 15)) * 15 END AS VARCHAR) AS Time) AS EndTime

    FROM TimePart)

    INSERT INTO busroutetime

    SELECT RouteNumber, DATEADD(mi,15*N,StartTime), CASE WHEN StartTime BETWEEN DATEADD(mi,15*N,StartTime) AND DATEADD(mi,15*N+1,StartTime) THEN 15 - StartMin % 15

    WHEN EndTime BETWEEN DATEADD(mi,15*N-1,StartTime) AND DATEADD(mi,15*N,StartTime) THEN EndMin % 15

    ELSE 15 END

    FROM StartEnd

    CROSS JOIN cteTally

    WHERE N <= DATEDIFF(MI,StartTime,EndTime) / 15

    SELECT BusRoute AS [Route], [9:30], [9:45], [10:00], [10:15]

    FROM (

    SELECT BusRoute, TimeOfDay, inservice

    FROM #busroutetime) p

    PIVOT

    ( SUM(inservice)

    FOR TimeOfDay IN ([9:30], [9:45], [10:00], [10:15])) AS PVT

    drop table #BusRouteTime, #BusRoutes

    I'm so grateful for your excellent help on this, it's been so very much appreciated, so I apologize for my less than stellar SQL understanding. I'm excited to see this coming closer to being finalized!

    Thanks again!

  • i do hope you go through and follow through the logic of what i did and don't just blindly use it. research CTE's and Pivot tables and try to recreate what i did from scratch. the way i learned most of what i know for SQL is by reading the forums and stepping through the posted code. it really helped to accelerate my learning.


    For faster help in answering any problems Please read How to post data/code on a forum to get the best help - Jeff Moden[/url] for the best way to ask your question.

    For performance Issues see how we like them posted here: How to Post Performance Problems - Gail Shaw[/url]

    Need to Split some strings? Jeff Moden's DelimitedSplit8K[/url]
    Jeff Moden's Cross tab and Pivots Part 1[/url]
    Jeff Moden's Cross tab and Pivots Part 2[/url]

  • I got it. (Didn't think about adding the original Tally cte into the existing one.)

    One question on how it's calculating: If i have a route starting at 0900 and ending at 0914, it shows 15 in the 0900 column when it should show 14. That seems to only happen to the routes that start and end in the same 15 min increment, (if the entire routetime is less than 15 minutes). Is that somehow inherent in how the time subtraction is happening or is there something I could adjust so that it's calculating those instances more precisely?

    This is excellent stuff. Thank you so much again. Obviously I did not have the SQL skill to put it together myself so your expertise is very much appreciated.

  • Took me a while to accomplish this. It turned out that the hardest part was getting the select criteria correct to properly align the schedule times to the buckets.

    Please take a look and let me know what you you all think.

    declare @ReportDate DATE = '2012-07-18';

    WITH

    /* Create the dynamic tally table (0 based) */

    e1(n) AS (SELECT n FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),

    e2(n) AS (SELECT 1 FROM e1 a CROSS JOIN e1 b),

    tally(n) AS (SELECT 0 UNION ALL SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM e2 a CROSS JOIN e2 b),

    /* Create the Time Buckets and the Start/End times to be used in time computations */

    TimeBuckets(DailyTime, DailyBeginTime, DailyEndTime) AS (

    SELECT TOP(DATEDIFF(MI,@ReportDate,DATEADD(dd,1,@ReportDate))/15)

    CAST(DATEADD(mi,15 * n, CAST(@ReportDate AS DATETIME)) AS TIME(0)),

    DATEADD(mi,15 * n, CAST(@ReportDate AS DATETIME)),

    DATEADD(mi,15 * (n + 1), CAST(@ReportDate AS DATETIME))

    FROM

    tally

    ),

    /* This CTE is the test data and can be removed from the query by replacing the source table in DailyBusRoutes CTE */

    TestData(RouteNumber, StartTime, EndTime) AS

    (SELECT

    RouteNumber,

    StartTime,

    EndTime

    FROM

    (VALUES (1234,'2012-07-18 09:00:00','2012-07-18 13:00:00'),

    (5678,'2012-07-18 10:13:00','2012-07-18 14:28:00'),

    (1321,'2011-07-17 23:30:00','2012-07-18 06:30:00'),

    (5342,'2012-07-18 21:14:00','2012-07-19 03:36:00'),

    (6565,'2012-07-17 12:12:00','2012-07-17 20:42:00'),

    (9897,'2012-07-19 06:12:00','2012-07-19 14:34:00')

    )dt(RouteNumber, StartTime, EndTime)

    WHERE

    @ReportDate between cast(StartTime as DATE) and cast(EndTime as DATE)

    ),

    /*********************************************************************************************************************/

    /* DailyBusRoutes provides the data that will be pivoted. Replace the CTE TestData with the source table */

    DailyBusRoutes as (

    SELECT

    left(cast(tb.DailyTime as varchar(10)),5) DailyTime,

    td2.RouteNumber,

    isnull(datediff(mi,case when td.StartTime > tb.DailyBeginTime then td.StartTime else tb.DailyBeginTime end,

    case when td.EndTime > tb.DailyEndTime then tb.DailyEndTime else td.EndTime end),0) as TimeScheduled

    FROM

    TimeBuckets tb

    CROSS JOIN TestData td2

    OUTER apply (SELECT

    *

    FROM TestData td1

    WHERE

    (td1.RouteNumber = td2.RouteNumber) AND

    (tb.DailyEndTime > td1.StartTime AND tb.DailyBeginTime < td1.EndTime )) td

    )

    select ReportDate,

    RouteNumber as [Route],[00:00],[00:15],[00:30],[00:45],

    [01:00],[01:15],[01:30],[01:45],

    [02:00],[02:15],[02:30],[02:45],

    [03:00],[03:15],[03:30],[03:45],

    [04:00],[04:15],[04:30],[04:45],

    [05:00],[05:15],[05:30],[05:45],

    [06:00],[06:15],[06:30],[06:45],

    [07:00],[07:15],[07:30],[07:45],

    [08:00],[08:15],[08:30],[08:45],

    [09:00],[09:15],[09:30],[09:45],

    [10:00],[10:15],[10:30],[10:45],

    [11:00],[11:15],[11:30],[11:45],

    [12:00],[12:15],[12:30],[12:45],

    [13:00],[13:15],[13:30],[13:45],

    [14:00],[14:15],[14:30],[14:45],

    [15:00],[15:15],[15:30],[15:45],

    [16:00],[16:15],[16:30],[16:45],

    [17:00],[17:15],[17:30],[17:45],

    [18:00],[18:15],[18:30],[18:45],

    [19:00],[19:15],[19:30],[19:45],

    [20:00],[20:15],[20:30],[20:45],

    [21:00],[21:15],[21:30],[21:45],

    [22:00],[22:15],[22:30],[22:45],

    [23:00],[23:15],[23:30],[23:45]

    from (

    select RouteNumber,DailyTime,TimeScheduled

    from DailyBusRoutes) p

    pivot (sum(TimeScheduled) for DailyTime in ([00:00],[00:15],[00:30],[00:45],

    [01:00],[01:15],[01:30],[01:45],

    [02:00],[02:15],[02:30],[02:45],

    [03:00],[03:15],[03:30],[03:45],

    [04:00],[04:15],[04:30],[04:45],

    [05:00],[05:15],[05:30],[05:45],

    [06:00],[06:15],[06:30],[06:45],

    [07:00],[07:15],[07:30],[07:45],

    [08:00],[08:15],[08:30],[08:45],

    [09:00],[09:15],[09:30],[09:45],

    [10:00],[10:15],[10:30],[10:45],

    [11:00],[11:15],[11:30],[11:45],

    [12:00],[12:15],[12:30],[12:45],

    [13:00],[13:15],[13:30],[13:45],

    [14:00],[14:15],[14:30],[14:45],

    [15:00],[15:15],[15:30],[15:45],

    [16:00],[16:15],[16:30],[16:45],

    [17:00],[17:15],[17:30],[17:45],

    [18:00],[18:15],[18:30],[18:45],

    [19:00],[19:15],[19:30],[19:45],

    [20:00],[20:15],[20:30],[20:45],

    [21:00],[21:15],[21:30],[21:45],

    [22:00],[22:15],[22:30],[22:45],

    [23:00],[23:15],[23:30],[23:45])) as pvt

    cross join (select @ReportDate) rpt(ReportDate);

  • tacy.highland (7/18/2012)


    I got it. (Didn't think about adding the original Tally cte into the existing one.)

    One question on how it's calculating: If i have a route starting at 0900 and ending at 0914, it shows 15 in the 0900 column when it should show 14. That seems to only happen to the routes that start and end in the same 15 min increment, (if the entire routetime is less than 15 minutes). Is that somehow inherent in how the time subtraction is happening or is there something I could adjust so that it's calculating those instances more precisely?

    This is excellent stuff. Thank you so much again. Obviously I did not have the SQL skill to put it together myself so your expertise is very much appreciated.

    that was because the query was not hitting any of the case statemnts and going to the else. here is a fixed query and i over looked it because i made the assumption (from my experience with public transportation) that the routes would all be over 15 min. here is the fixed query

    SELECT RouteNumber, DATEADD(mi,15*N,StartBase), CASE WHEN StartBase = EndBase THEN EndMin - StartMin

    WHEN StartBase = DATEADD(mi,15*N,StartBase) THEN 15 - StartMin % 15

    WHEN EndBase = DATEADD(mi,15*N,StartBase) THEN EndMin % 15

    ELSE 15 END

    FROM StartEnd

    CROSS JOIN Tally

    WHERE N <= DATEDIFF(MI,StartBase,EndBase) / 15

    i also realized that i was actually using the 15 min base and not the actual start or end time (it was working so i did not test much, that will teach me) so i dropped the between and just have an =.

    Lynn's solution is actually better as i did not take into account bus routes running longer than 24 hours. more assumptions and ways my solution breaks.


    For faster help in answering any problems Please read How to post data/code on a forum to get the best help - Jeff Moden[/url] for the best way to ask your question.

    For performance Issues see how we like them posted here: How to Post Performance Problems - Gail Shaw[/url]

    Need to Split some strings? Jeff Moden's DelimitedSplit8K[/url]
    Jeff Moden's Cross tab and Pivots Part 1[/url]
    Jeff Moden's Cross tab and Pivots Part 2[/url]

  • This is great stuff. Thanks everyone for your contributions.

    I have another question now. Since we've been using smalldatetime as the startime and endtime format, (but I see it's being converted to varchar at one point in the process and then converted to time), would it make much difference if the startime/endtime were actually varchar of just time, like this: 0900, 1700? Rather than the DatePart being done in the TimePart cte, we could just do LEFT(startime,2) and RIGHT(startime,2) and the code would work the same, right? It looked like everything beyond that was doing the proper conversions for calculations.

  • tacy.highland (7/19/2012)


    This is great stuff. Thanks everyone for your contributions.

    I have another question now. Since we've been using smalldatetime as the startime and endtime format, (but I see it's being converted to varchar at one point in the process and then converted to time), would it make much difference if the startime/endtime were actually varchar of just time, like this: 0900, 1700? Rather than the DatePart being done in the TimePart cte, we could just do LEFT(startime,2) and RIGHT(startime,2) and the code would work the same, right? It looked like everything beyond that was doing the proper conversions for calculations.

    Store date/time values as date/time values. Sorry, but I strongly believe that you should use the appropriate data type for the data. Yes, there are conversions being done to character values and such, but the base data should still be the appropriate data type for the data.

  • I agree, however, I'm now up against a constraint where the start/end time data that i'll be receiving is in the varchar format which I can't do anything about.

    I don't know much as far as efficiencies to be gained using specific data types, but given this specific constraint, is there something more to be done to make things work better or would the LEFT(startime,2)/RIGHT(startime,2) end up working ok? ie should there be an initial conversion of the data from varchar to time in another declare statement maybe?

  • Here is the problem, I don't know. You didn't provide any DDL or sample data for this problem. The sample data I used was provided by capn.hector.

    If you are receiving this data in a file, I'd convert it to the appropriate data type when importing it.

  • I did provide sample data yesterday, though not much. Capn's data was essentially the same anyway. And the sample data actually was in the smalldatetime format, so this line of questioning was a bit of a tangent from that. I'll try to get the data converted and see how it goes.

    Thanks for the feedback though. It certainly helps to hear from the experts out there and is very much appreciated.

    😀

  • tacy.highland (7/19/2012)


    I did provide sample data yesterday, though not much. Capn's data was essentially the same anyway. And the sample data actually was in the smalldatetime format, so this line of questioning was a bit of a tangent from that. I'll try to get the data converted and see how it goes.

    Thanks for the feedback though. It certainly helps to hear from the experts out there and is very much appreciated.

    😀

    Tacy,

    Going to disagree a bit regarding the providing of sample data. I went back and could find nothing that was actually usable. The following would have been helpful as it would allow anyone to simply cut/paste and run the code to establish a working test environment for the problem.

    IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[RouteTimes]') AND type in (N'U'))

    DROP TABLE [dbo].[RouteTimes]

    GO

    CREATE TABLE dbo.RouteTimes (

    RouteNumber INT,

    StartDatetime DATETIME,

    EndDatetime DATETIME

    );

    INSERT INTO dbo.RouteTimes (

    RouteNumber,

    StartDatetime,

    EndDatetime

    )

    VALUES (1234,'2012-07-18 09:00:00','2012-07-18 13:00:00'),

    (5678,'2012-07-18 10:13:00','2012-07-18 14:28:00'),

    (1321,'2011-07-17 23:30:00','2012-07-18 06:30:00'),

    (5342,'2012-07-18 21:14:00','2012-07-19 03:36:00'),

    (6565,'2012-07-17 12:12:00','2012-07-17 20:42:00'),

    (9897,'2012-07-19 06:12:00','2012-07-19 14:34:00');

    SELECT * FROM dbo.RouteTimes; -- Verify the data

    GO

  • I'm confused.

    I followed the instructions listed in the Best Practices link:

    http://www.sqlservercentral.com/articles/Best+Practices/61537/

    And posted the code yesterday at 834pm. Aside from the Set Identity statements (which do err out) it worked for me to copy,paste and run to create a table with data. This didn't work for you?

    CREATE TABLE #mytable

    (

    Route INT

    , StartTime DATETIME

    , EndTime DATETIME

    )

    --SET IDENTITY_INSERT #mytable ON

    INSERT INTO #mytable

    (Route

    , StartTime

    , EndTime

    )

    SELECT '601','Jul 17 2012 9:00AM','Jul 17 2012 11:30AM'

    UNION ALL

    SELECT '602','Jul 17 2012 9:15AM','Jul 17 2012 12:00PM'

    --SET IDENTITY_INSERT #mytable OFF

    I used the Select/UnionAll as specified in the article to get the actual data from the existing table I had here.

  • tacy.highland (7/19/2012)


    I'm confused.

    I followed the instructions listed in the Best Practices link:

    http://www.sqlservercentral.com/articles/Best+Practices/61537/

    And posted the code yesterday at 834pm. Aside from the Set Identity statements (which do err out) it worked for me to copy,paste and run to create a table with data. This didn't work for you?

    CREATE TABLE #mytable

    (

    Route INT

    , StartTime DATETIME

    , EndTime DATETIME

    )

    --SET IDENTITY_INSERT #mytable ON

    INSERT INTO #mytable

    (Route

    , StartTime

    , EndTime

    )

    SELECT '601','Jul 17 2012 9:00AM','Jul 17 2012 11:30AM'

    UNION ALL

    SELECT '602','Jul 17 2012 9:15AM','Jul 17 2012 12:00PM'

    --SET IDENTITY_INSERT #mytable OFF

    I used the Select/UnionAll as specified in the article to get the actual data from the existing table I had here.

    Are you talking about this?

    SELECT *

    ,(LEFT(dbo.FX_SmallDateTime_To_Time_Char4(Start_Time),2) * 60) + RIGHT(dbo.FX_SmallDateTime_To_Time_Char4(Start_Time),2) Start_Time_Min

    ,(LEFT(dbo.FX_SmallDateTime_To_Time_Char4(End_Time),2) * 60) + RIGHT(dbo.FX_SmallDateTime_To_Time_Char4(End_Time),2) End_Time_Min

    INTO #temp

    FROM [dbo].[Schedules_RC]

    SELECT *,

    CASE WHEN Start_Time_Min BETWEEN 524 AND 539 THEN

    CASE WHEN (End_Time_Min - Start_Time_Min) >= 15 THEN 15 ELSE (End_Time_Min - Start_Time_Min) END

    ELSE 0

    END [0845]

    ,CASE WHEN Start_Time_Min BETWEEN 540 AND 555 THEN

    CASE WHEN (End_Time_Min - Start_Time_Min) >= 15 THEN 15 ELSE (End_Time_Min - Start_Time_Min) END

    ELSE 0

    END [0900]

    ,CASE WHEN Start_Time_Min BETWEEN 556 AND 570 THEN

    CASE WHEN (End_Time_Min - Start_Time_Min) >= 15 THEN 15 ELSE (End_Time_Min - Start_Time_Min) END

    ELSE 0

    END [0915]

    ,(End_Time_Min - Start_Time_Min)

    FROM #temp

    If so, try running it in an empty sandbox database. When I run it in my sandbox database I get the following error:

    Msg 208, Level 16, State 1, Line 1

    Invalid object name 'dbo.Schedules_RC'.

  • Or is this what you are talking about? Not sure how I missed it.

    Declare @Schedule table (Starttm datetime, endtm datetime)

    Insert @Schedule values ( '2012-02-16 02:00:00', '2012-02-16 04:00:00')

    , ('2012-02-16 02:00:00', '2012-02-16 05:00:00')

    , ('2012-02-16 03:00:00', '2012-02-16 06:00:00')

    , ('2012-02-16 08:00:00', '2012-02-16 09:00:00')

    Select *, (Select count(*) from @Schedule where Incr between StartTm and Endtm) as onclock -- DateAdd(ms, -3, Endtm)

    from

    (Select Seq, dateadd(minute, seq * 15, '2012-02-16') as Incr -- Add 15 minutes 95 times, to make up a whole day.

    from

    (

    select top 95 row_number() over(partition by 1 order by A.name) as Seq

    from syscolumns A -- This is one quick and dirty way for a list of sequential numbers

    ) as DQ

    ) as DQ1

    order by incr

    If this is the data, it changes how I would need to look at doing my code. Also, not sure how to use the data in the onclock column. Need to look at it a bit more.

Viewing 15 posts - 16 through 30 (of 81 total)

You must be logged in to reply to this topic. Login to reply