I'd like to create a table that has one column and the column is weekly dates starting with 1/1/2015 thru the end of 2015. Is there an easy way to do this?
ie
CREATE TABLE Week_End_Dates
(
WeekEndDt date
);
INSERT INTO Week_End_Dates(WeekEndDt)
VALUES ('2015-01-01'),('2015-01-08')
There are plenty of ways to generate a date sequence. One is to use a common table expression like this:
WITH CTE (DT) AS
(
SELECT CAST('2015-01-01' AS DATE) DT
UNION ALL
SELECT DATEADD(WEEK, 1, DT)
FROM CTE
WHERE DATEADD(WEEK, 1, DT) < '2016-01-01'
)
INSERT INTO Week_End_Dates(WeekEndDt)
SELECT * FROM CTE
It's not the most efficient (and can easily be optimized in several ways), but it should do what you seem to want.
The query would generate a result like:
WeekEndDt
2015-01-01
2015-01-08
2015-01-15
2015-01-22
2015-01-29
... etc until 2015-12-31
Related
Suppose you have a table like:
id subscription_start subscription_end segment
1 2016-12-01 2017-02-01 87
2 2016-12-01 2017-01-24 87
...
And wish to generate a temporary table with months.
One way would be to encode the month date as:
with months as (
select
'2016-12-01' as 'first',
'2016-12-31' as 'last'
union
select
'2017-01-01' as 'first',
'2017-01-31' as 'last'
...
) select * from months;
So that I have an output table like:
first_day last_day
2017-01-01 2017-01-31
2017-02-01 2017-02-31
2017-03-01 2017-03-31
I would like to generate a temporary table with a custom interval (above), without manually encoding all the dates.
Say the interval is of 12 months, for each year, for as many years there are in the db.
I'd like to have general approach to compute the months table with the same output as above.
Or, one may adjust the range to a custom interval (months split an year in 12 parts, but one may want to split a time in a custom interval of days).
To start, I was thinking to use recursive query like:
with months(id, first_day, last_day, month) as (
select
id,
first_day,
last_day,
0
where
subscriptions.first_day = min(subscriptions.first_day)
union all
select
id,
first_day,
last_day,
months.month + 1
from
subscriptions
left join months on cast(
strftime('%m', datetime(subscriptions.subscription_start)) as int
) = months.month
where
months.month < 13
)
select
*
from
months
where
month = 1;
but it does not do what I'd expect: here I was attempting to select the first row from the table with the minimum date, and populate a table at interval of months, ranging from 1 to 12. For each month, I was comparing the string date field of my table (e.g. 2017-03-01 = 3 is march).
The query above does work and also seems a bit complicated, but for the sake of learning, which alternative would you propose to create a temporary table months without manually coding the intervals ?
I know how I would do this with something like SAS, but if I wanted to create a table that had as many rows as there were month intervals derived from this statement:
cast((cast(2017-03-31 as date) - cast(2016-01-31 as date) month(4)) as int) as date_range
....to give an output like this:
2017-03-31
2017-02-28
2017-01-31
2017-12-31
2017-11-30
2017-10-31
2017-09-30
2017-08-31
2017-07-31
2017-06-30
2017-05-31
2017-04-30
What statement would I need to do this in Teradata?
Thanks
Are those dates calulated based on existing columns?
Or do you just need that list?
In both cases you can utilze Teradata's proprietary EXPAND ON feature:
SELECT BEGIN(pd)
FROM SYS_CALENDAR.CALENDAR -- your table here
WHERE calendar_date = DATE -- EXPAND requires FROM, so this is just to get a single row
EXPAND ON PERIOD(date '2016-01-31' -- start date
,date '2017-03-31' + 1 -- end date (+1 because it's not included in the date range)
) AS pd BY ANCHOR PERIOD MONTH_END -- one row for each month end within the period
It is safer to get the first of the month because adding months to the last day of the month can be problematic (if you started with '2016-02-29' you would get the 29th of succeeding months).
You can do what you want with a recursive cte:
with recursive cte(dte) as (
select cast('2016-02-01' as date)
union all
select add_months(cte.dte, 1)
from cte
where dte <= '2017-05-01'
),
dates as (
select dte - interval '1 day'
from cte
)
. . .
I am currently working on a query to calculate total no of days between date ranges by year
Table:
Start Date End Date
01/01/2013 04/30/2014
11/01/2014 05/31/2015
06/01/2015 12/31/2015
My expected result.
2013 - 365
2014 - 180
2015 - 365
I can do this in multiple steps using temp table. Is there any simple way to do this calculation.
Thanks
Okay so try this out:
SELECT * INTO #yourTable
FROM
(
SELECT CAST('01/01/2013' AS DATE),CAST('04/30/2014' AS DATE) UNION ALL
SELECT '11/01/2014','05/31/2015' UNION ALL
SELECT '06/01/2015','12/31/2015'
) A(StartDate,EndDate);
DECLARE #MaxEndDate DATE = (SELECT MAX(EndDate) FROM #yourTable);
WITH CTE_Dates
AS
(
SELECT MIN(StartDate) dates
FROM #yourTable
UNION ALL
SELECT DATEADD(Day,1,dates)
FROM CTE_Dates
WHERE dates < #MaxEndDate
)
SELECT YEAR(dates) yr,COUNT(DATES) cnt
FROM #yourTable A
CROSS APPLY(SELECT dates FROM CTE_Dates WHERE dates BETWEEN A.startDate AND A.EndDate) CA
GROUP BY YEAR(dates)
OPTION (MAXRECURSION 0)
PRINT DATEDIFF(DAY, '1/1/2013', '4/30/2014')
This will give you what you are looking for. Just repeat for all the dates that you want.
This gives the number of times the midnight boundary is crossed between the two dates. You may decide to need to add one to this if you're including both dates in the count - or subtract one if you don't want to include either date.
How can I enumerate multiple date ranges in SQL Server 2008? I know how to do this if my table contains a single record
StartDate EndDate
2014-01-01 2014-01-03
;WITH DateRange
AS (
SELECT #StartDate AS [Date]
UNION ALL
SELECT DATEADD(d, 1, [Date])
FROM DateRange
WHERE [Date] < #EndDate
)
SELECT * FROM DateRange
OUTPUT
2014-01-01, 2014-01-02, 2014-01-03
I am however lost as how to do it if my table contains multiple records. I could possibly use the above logic in a cursor but want to know if there is a set based solution instead.
StartDate EndDate
2014-01-01 2014-01-03
2014-01-05 2014-01-06
DESIRED OUTPUT:
2014-01-01, 2014-01-02, 2014-01-03, 2014-01-05, 2014-01-06
Well, let's see. Define the ranges as a table. Then generate the full range of dates from the first to the last date. Finally, select the dates that are in the range:
with dateranges as (
select cast('2014-01-01' as date) as StartDate, cast('2014-01-03' as date) as EndDate union all
select '2014-01-05', '2014-01-06'
),
_dates as (
SELECT min(StartDate) AS [Date], max(EndDate) as enddate
FROM dateranges
UNION ALL
SELECT DATEADD(d, 1, [Date]), enddate
FROM _dates
WHERE [Date] < enddate
),
dates as (
select [date]
from _dates d
where exists (select 1 from dateranges dr where d.[date] >= dr.startdate and d.[date] <= dr.enddate)
)
select *
from dates
. . .
You can see this work here.
You could grab the min and max dates first, like so:
SELECT #startDate = MIN(StartDate), #endDate = MAX(EndDate)
FROM YourTable
WHERE ...
And then pass those variables into your date range enumerator.
Edit... Whoops, I missed an important requirement. See the accepted answer.
As GordonLinoff mentioned, you should:
Store your ranges in a table
Generate a range of dates that encompasses your ranges
Filter down to only those dates that fall within the range
The following query builds up a collection of numbers, and then uses that to quickly generate all of the dates that fall within each range.
-- Create a table of digits (0-9)
DECLARE #Digits TABLE (digit INT NOT NULL PRIMARY KEY);
INSERT INTO #Digits(digit)
VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
WITH
-- Store our ranges in a common table expression
CTE_DateRanges(StartDate, EndDate) AS (
SELECT '2014-01-01', '2014-01-03'
UNION ALL
SELECT '2014-01-05', '2014-01-06'
)
SELECT DATEADD(DAY, NUMBERS.num, RANGES.StartDate) AS Date
FROM
(
-- Create the list of all 3-digit numbers (0-999)
SELECT D3.digit * 100 + D2.digit * 10 + D1.digit AS num
FROM #Digits AS D1
CROSS JOIN #Digits AS D2
CROSS JOIN #Digits AS D3
-- Add more CROSS JOINs to #Digits if your ranges span more than 999 days
) NUMBERS
-- Join to our ranges table to generate the dates and filter them
-- down to those that fall within a range
INNER JOIN CTE_DateRanges RANGES
ON DATEADD(DAY, NUMBERS.num, RANGES.StartDate) <= RANGES.EndDate
ORDER BY
Date
The date creation is done by joining our number list with our date ranges, using the number as a number of days to add to the StartDate of the range. We then filter out any results where the generated date for a given range falls beyond that range's EndDate. Since we're adding a non-negative number of days to the StartDate to generate the date, we know that our date will always be greater-than-or-equal-to the StartDate of the range, so we don't need to include StartDate in the WHERE clause.
This query will return DATETIME values. If you need a DATE value, rather than a DATETIME value, you can simply cast the value in the SELECT clause.
Credit goes to Itzik Ben-Gan for the digits table.
I have the following
id eventid startdate enddate
1 1 2009-01-03 2009-01-05
1 2 2009-01-05 2009-01-09
1 3 2009-01-12 2009-01-15
How to generate the missing dates pertaining to every eventid?
Edit:
The missing gaps are to be find out based on the eventid's. e.g. for eventid 1 the output should be 1/3/2009,1/4/2009,1/5/2009.. for eventtype id 2 it will be 1/5/2009, 1/6/2009... to 1/9/2009 etc
My task is to find out the missing dates between two given dates.
Here is the whole thing which i have done so far
declare #tblRegistration table(id int primary key,startdate date,enddate date)
insert into #tblRegistration
select 1,'1/1/2009','1/15/2009'
declare #tblEvent table(id int,eventid int primary key,startdate date,enddate date)
insert into #tblEvent
select 1,1,'1/3/2009','1/5/2009' union all
select 1,2,'1/5/2009','1/9/2009' union all
select 1,3,'1/12/2009','1/15/2009'
;with generateCalender_cte as
(
select cast((select startdate from #tblRegistration where id = 1 )as datetime) DateValue
union all
select DateValue + 1
from generateCalender_cte
where DateValue + 1 <= (select enddate from #tblRegistration where id = 1)
)
select DateValue as missingdates from generateCalender_cte
where DateValue not between '1/3/2009' and '1/5/2009'
and DateValue not between '1/5/2009' and '1/9/2009'
and DateValue not between '1/12/2009'and'1/15/2009'
Actually what I am trying to do is that, I have generated a calender table and from there I am trying to find out the missing dates based on the id's
The ideal output will be
eventid missingdates
1 2009-01-01 00:00:00.000
1 2009-01-02 00:00:00.000
3 2009-01-10 00:00:00.000
3 2009-01-11 00:00:00.000
and also it has to be in SET BASED and the start and end dates should not be hardcoded
Thanks in adavnce
The following uses a recursive CTE (SQL Server 2005+):
WITH dates AS (
SELECT CAST('2009-01-01' AS DATETIME) 'date'
UNION ALL
SELECT DATEADD(dd, 1, t.date)
FROM dates t
WHERE DATEADD(dd, 1, t.date) <= '2009-02-01')
SELECT t.eventid, d.date
FROM dates d
JOIN TABLE t ON d.date BETWEEN t.startdate AND t.enddate
It generates dates using the DATEADD function. It can be altered to take a start & end date as parameters. According to KM's comments, it's faster than using the numbers table trick.
Like rexem - I made a function that contains a similar CTE to generate any series of datetime intervals you need. Very handy for summarizing data by datetime intervals like you are doing.
A more detailed post and the function source code are here:
Insert Dates in the return from a query where there is none
Once you have the "counts of events by date" ... your missing dates would be the ones with a count of 0.