Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

T-SQL - Filtered dates between a range

Code:

DECLARE @T1 TABLE ([ID] INT, [WD] DATE, [SD] DATETIME, [ED] DATETIME, [Val] INT)
INSERT INTO @T1 ( [ID], [WD], [SD], [ED], [VAL] )
VALUES  ( 1, '2016-02-14', '2016-02-14 08:00:00', '2016-02-14 16:30:00', 12 )
        ,( 1, '2016-02-14', '2016-02-16 08:00:00', '2016-02-16 16:30:00', 15 )
        ,( 3, '2016-02-14', '2016-02-16 08:00:00', '2016-02-16 16:30:00', 12 )
        ,( 1, '2016-02-21', '2016-02-22 08:00:00', '2016-02-22 16:30:00', 100 )
        ,( 2, '2016-02-21', '2016-02-25 08:00:00', '2016-02-25 16:30:00', 124 )
        ,( 3, '2016-03-20', '2016-03-21 08:00:00', '2016-03-21 16:30:00', 10 )
        ,( 3, '2016-04-17', '2016-04-17 08:00:00', '2016-04-17 16:30:00', 8 );

DECLARE @StartDate DATE
        , @EndDate DATE;

SELECT @StartDate = MIN(WD) FROM @T1
SELECT @EndDate = DATEADD(DAY,6,MAX(WD)) FROM @T1;

DECLARE @T2 TABLE (D DATE) -- Table generated to get ALL dates between the minium WD (Min Sunday) and 6 days after the maximum WD (Max Sunday + 6 days to complete the week date range)
INSERT INTO @T2 ( [D] )
SELECT  DATEADD(DAY, NBR - 1, @StartDate)
FROM    ( SELECT ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS NBR
          FROM   sys.columns c
        ) NBR
WHERE   NBR - 1 <= DATEDIFF(DAY, @StartDate, @EndDate);

SELECT * FROM @T1;
SELECT * FROM @T2;

Description: @T1 is the pre-populated source data table with specific dates where [ID] = an ID field of some sort, [WD] = Different start of the week (Sundays), [SD] = Start of a day within that [WD] week, [ED] = End of a day within that [WD] week, [Val] = Some assigned value for that ID/WD/SD/ED record

@T2 is the table I'm defining and populating to get all the dates within MIN([WD]) and MAX([WD]) + 6 days (to compelte the week)

Goal:

Return @T1 data along with any missing dates for those [ID]/[WD] weeks. (refer to Desired Output for better understanding)

Desired result (copied from Excel so the format is different, but that's ok)

[ID][WD]        [SD]            [ED]            [Val]
1   2/14/2016   2/14/16 8:00    2/14/16 16:30   12
1   2/14/2016   2/15/16 00:00   2/15/16 00:00   NULL
1   2/14/2016   2/16/16 8:00    2/16/16 16:30   15
1   2/14/2016   2/17/16 00:00   2/17/16 00:00   NULL
1   2/14/2016   2/18/16 00:00   2/18/16 00:00   NULL
1   2/14/2016   2/19/16 00:00   2/19/16 00:00   NULL
1   2/14/2016   2/20/16 00:00   2/20/16 00:00   NULL
3   2/14/2016   2/14/16 00:00   2/14/16 00:00   NULL
3   2/14/2016   2/15/16 00:00   2/15/16 00:00   NULL
3   2/14/2016   2/16/16 8:00    2/16/16 16:30   12
3   2/14/2016   2/17/16 00:00   2/17/16 00:00   NULL
3   2/14/2016   2/18/16 00:00   2/18/16 00:00   NULL
3   2/14/2016   2/19/16 00:00   2/19/16 00:00   NULL
3   2/14/2016   2/20/16 00:00   2/20/16 00:00   NULL
1   2/21/2016   2/21/16 00:00   2/21/16 00:00   NULL
1   2/21/2016   2/22/16 8:00    2/22/16 16:30   100
1   2/21/2016   2/23/16 00:00   2/23/16 00:00   NULL
1   2/21/2016   2/24/16 00:00   2/24/16 00:00   NULL
1   2/21/2016   2/25/16 00:00   2/25/16 00:00   NULL
1   2/21/2016   2/26/16 00:00   2/26/16 00:00   NULL
1   2/21/2016   2/27/16 00:00   2/27/16 00:00   NULL
2   2/21/2016   2/21/16 00:00   2/21/16 00:00   NULL
2   2/21/2016   2/22/16 00:00   2/22/16 00:00   NULL
2   2/21/2016   2/23/16 00:00   2/23/16 00:00   NULL
2   2/21/2016   2/24/16 00:00   2/24/16 00:00   NULL
2   2/21/2016   2/25/16 8:00    2/25/16 16:30   124
2   2/21/2016   2/26/16 00:00   2/26/16 00:00   NULL
2   2/21/2016   2/27/16 00:00   2/27/16 00:00   NULL
3   3/20/2016   3/20/16 00:00   3/20/16 00:00   NULL
3   3/20/2016   3/21/16 8:00    3/21/16 16:30   10
3   3/20/2016   3/22/16 00:00   3/22/16 00:00   NULL
3   3/20/2016   3/23/16 00:00   3/23/16 00:00   NULL
3   3/20/2016   3/24/16 00:00   3/24/16 00:00   NULL
3   3/20/2016   3/25/16 00:00   3/25/16 00:00   NULL
3   3/20/2016   3/26/16 00:00   3/26/16 00:00   NULL
3   4/17/2016   4/17/16 8:00    4/17/16 16:30   8
3   4/17/2016   4/18/16 00:00   4/18/16 00:00   NULL
3   4/17/2016   4/19/16 00:00   4/19/16 00:00   NULL
3   4/17/2016   4/20/16 00:00   4/20/16 00:00   NULL
3   4/17/2016   4/21/16 00:00   4/21/16 00:00   NULL
3   4/17/2016   4/22/16 00:00   4/22/16 00:00   NULL
3   4/17/2016   4/23/16 00:00   4/23/16 00:00   NULL

I tried using cross join and stuff but no luck with getting the desired output.

like image 351
007 Avatar asked Dec 29 '16 19:12

007


People also ask

Where are the date and time stored in SQL Server?

The date and time are collectively stored in a column using the datatype DATETIME2. SELECT * FROM TABLE_NAME WHERE DATE_TIME_COLUMN BETWEEN 'STARTING_DATE_TIME' AND 'ENDING_DATE_TIME';

How can I generate a range of dates in SQL?

To generate a range of dates you could write a table-valued function. This is a function that creates a date dimension for a data warehouse - you could probably adapt it fairly readily by trimming out the specials. Edit: Here it is without the date dimension hierarchy.

How to retrieve rows based on date and time in SQL?

In SQL, some problems require us to retrieve rows based on their dates and times. For such cases, we use the DATETIME2 datatype present in SQL. For this article, we will be using the Microsoft SQL Server as our database. Note – Here, we will use the WHERE and BETWEEN clauses along with the query to limit our rows to the given time.

What is the pattern of saving date and time in SQL Server?

The pattern of saving date and time in MS SQL Server is yyyy:mm: dd hh:mm: ss. The time is represented in a 24-hour format. The date and time are collectively stored in a column using the datatype DATETIME2. SELECT * FROM TABLE_NAME WHERE DATE_TIME_COLUMN BETWEEN 'STARTING_DATE_TIME' AND 'ENDING_DATE_TIME';


2 Answers

I removed your @T2 and use my @T3 instead.

DECLARE @T1 TABLE ([ID] INT, [WD] DATE, [SD] DATETIME, [ED] DATETIME, [Val] INT)
INSERT INTO @T1 ( [ID], [WD], [SD], [ED], [VAL] )
VALUES  ( 1, '2016-02-14', '2016-02-14 08:00:00', '2016-02-14 16:30:00', 12 )
        ,( 1, '2016-02-14', '2016-02-16 08:00:00', '2016-02-16 16:30:00', 15 )
        ,( 3, '2016-02-14', '2016-02-16 08:00:00', '2016-02-16 16:30:00', 12 )
        ,( 1, '2016-02-21', '2016-02-22 08:00:00', '2016-02-22 16:30:00', 100 )
        ,( 2, '2016-02-21', '2016-02-25 08:00:00', '2016-02-25 16:30:00', 124 )
        ,( 3, '2016-03-20', '2016-03-21 08:00:00', '2016-03-21 16:30:00', 10 )
        ,( 3, '2016-04-17', '2016-04-17 08:00:00', '2016-04-17 16:30:00', 8 );

DECLARE @T3 TABLE ([ID] INT, [WD] DATE, [SD] DATETIME, [ED] DATETIME);
WITH cte AS (
SELECT DISTINCT ID, WD
FROM
    @T1
)
INSERT INTO @T3 (ID, WD, SD, ED)
SELECT ID, WD, DATEADD(DAY,n,WD), DATEADD(DAY,n,WD)
FROM
    cte
    CROSS JOIN (VALUES (0),(1),(2),(3),(4),(5),(6)) AS ADDED(n);

SELECT t3.ID, t3.WD,COALESCE(t1.SD,t3.SD) AS SD, COALESCE(t1.ED,t3.ED) AS ED, t1.Val
FROM @T3 t3
    LEFT JOIN @T1 t1 ON t3.ID=t1.ID AND CONVERT(DATE,t1.SD)=CONVERT(DATE,t3.SD)
like image 131
DVT Avatar answered Sep 29 '22 05:09

DVT


This would be a left join of your source data against the timeline you created.

I could not figure out how the ID is assigned, but when I use COALESCE to fill up missing values this returns your desired output (except ID):

SELECT  [t1].[ID]
       ,COALESCE([t1].[WD], DATEADD(DAY, 1 - DATEPART(WEEKDAY, [t2].[D]), [t2].[D])) AS WD
       ,COALESCE([t1].[SD], [t2].[D]) AS SD
       ,COALESCE([t1].[ED], [t2].[D]) AS ED
       ,[t1].[Val]
FROM    @T2 [t2]
LEFT JOIN @T1 [t1]
ON      [t2].[D] = CONVERT(DATE, [t1].[SD]);
like image 21
zwitschi Avatar answered Sep 29 '22 07:09

zwitschi