SQL Convert Week Number to Date (dd/MM) - sql

I am trying to convert the week number (for example: 21) in SQL-Server to the date (from the Monday of that week) in dd/MM format.
I have searched online but cannot seem to find anything that I could use.
Is this something I can do?
Any help or advice is appreciated.
Thank you in advance.

Try this,
declare #wk int set #wk = 21
declare #yr int set #yr = 2016
select dateadd (week, #wk-1, dateadd (year, #yr-1900, 0)) - 4 -
datepart(dw, dateadd (week, #wk-1, dateadd (year, #yr-1900, 0)) - 4) + 1
or try this way
declare #wk int = 21
select dateadd(week,#wk-1, DATEADD(wk, DATEDIFF(wk,-1,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)), 0))

You can do it something like:
declare #Week_Number int, #Year int, #Year_Start_Day date, #Week_Day date
select
#Week_Number = 1,
#Year = 2016
select #Year_Start_Day = cast(#Year as nvarchar(4)) + '0101'
select #Week_Day = dateadd(wk, #Week_Number, #Year_Start_Day)
select dateadd(dd, 1 - datepart(weekday, #Week_Day), #Week_Day)

This will do:
DECLARE #y int = 2016,
#w int = 21
SELECT CONVERT(nvarchar(5),DATEADD(day,#w*7-(DATEPART(WEEKDAY,CAST(#y as nvarchar(4))+'-01-01')-2),CAST(#y as nvarchar(4))+'-01-01'),3)
Output:
23/05

How about this?
DECLARE #YearNum SMALLINT = 2016;
DECLARE #WeekNum TINYINT=25;
select
SUBSTRING(CONVERT(VARCHAR(10),selected_date,105),0,6) AS WeeKDate
from
(select DATEADD(dd,t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i,'1970-01-01') selected_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where YEAR(selected_date)=#YearNum
AND DATEPART(WK,selected_date)=#WeekNum
AND DATEPART(WEEKDAY,selected_date)=2 -- Monday

Related

SQL: Create multiple rows for a record based on months between two dates

My table has records as below for different Id's and different start and end dates
ID, Startdate, Enddate
1, 2017-02-14, 2018-11-05
I want to write an SQL without using date dimension table that gives below output: Basically one record for each month between start and end date.
1, 2017, 02
1, 2017, 03
1, 2017, 04
1, 2017, 05
1, 2017, 06
1, 2017, 07
1, 2017, 08
1, 2017, 09
1, 2017, 10
1, 2017, 11
1, 2017, 12
1, 2018, 01
1, 2018, 02
1, 2018, 03
1, 2018, 04
1, 2018, 05
1, 2018, 06
1, 2018, 07
1, 2018, 09
1, 2018, 10
1, 2018, 11
Please use below query example:
set #start_date = '2017-02-14';
set #end_date = LAST_DAY('2018-11-05');
WITH RECURSIVE date_range AS
(
select MONTH(#start_date) as month_, YEAR(#start_date) as year_, DATE_ADD(#start_date, INTERVAL 1 MONTH) as next_month_date
UNION
SELECT MONTH(dr.next_month_date) as month_, YEAR(dr.next_month_date) as year_, DATE_ADD(dr.next_month_date, INTERVAL 1 MONTH) as next_month_date
FROM date_range dr
where next_month_date <= #end_date
)
select month_, year_ from date_range
order by next_month_date desc
This is what I did and it worked like a charm:
-- sample data
WITH table_data
AS (
SELECT 1 AS id
,cast('2017-08-14' AS DATE) AS start_dt
,cast('2018-12-16' AS DATE) AS end_dt
UNION ALL
SELECT 2 AS id
,cast('2017-09-14' AS DATE) AS start_dt
,cast('2019-01-16' AS DATE) AS end_dt
)
-- find minimum date from the data
,starting_date (start_date)
AS (
SELECT min(start_dt)
FROM TABLE_DATA
)
--get all months between min and max dates
,all_dates
AS (
SELECT last_day(add_months(date_trunc('month', start_date), idx * 1)) month_date
FROM starting_date
CROSS JOIN _v_vector_idx
WHERE month_date <= add_months(start_date, abs(months_between((
SELECT min(start_dt) FROM TABLE_DATA), (SELECT max(end_dt) FROM TABLE_DATA))) + 1)
ORDER BY month_date
)
SELECT id
,extract(year FROM month_date)
,extract(month FROM month_date)
,td.start_dt
,td.end_dt
FROM table_data td
INNER JOIN all_dates ad
ON ad.month_date > td.start_dt
AND ad.month_date <= last_day(td.end_dt)
ORDER BY 1
,2
You have to generate date and from that have to pick year and month
select distinct year(date),month( date) from
(select * from (
select
date_add('2017-02-14 00:00:00.000', INTERVAL n5.num*10000+n4.num*1000+n3.num*100+n2.num*10+n1.num DAY ) as date
from
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n1,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n2,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n3,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n4,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n5
) a
where date >'2017-02-14 00:00:00.000' and date < '2018-11-05'
) as t

SQL - How to get a complete month calendar table

I need to generate a report that will show 7 Columns Monday to Sunday and 5 Rows, just like a normal month calendar:
I need the dates in a list table, so the desire table result should look like this:
That way my report designer will automatically create a view like above.
The above table result should be generated on the fly with no need of any database table because after this I will have to left join the dates to one of my db tables so I can populate other kind of information that I will use to display in my report for each day.
Any clue?
SQL Fiddle for arbitrary sequence of dates, no tables required.
http://sqlfiddle.com/#!3/9eecb7/275
DECLARE #dtStartDate datetime
DECLARE #dtEndDate datetime
SET #dtStartDate = '2015-05-01'
SET #dtEndDate = '2015-05-31'
SELECT
CASE DATEPART(weekday, T.DateVal)
WHEN 1 THEN 'Sunday'
WHEN 2 THEN 'Monday'
WHEN 3 THEN 'Tuesday'
WHEN 4 THEN 'Wednesday'
WHEN 5 THEN 'Thursday'
WHEN 6 THEN 'Friday'
WHEN 7 THEN 'Saturday'
END AS WeekDay,
DATEPART(day, T.DateVal) AS [Date],
DATEPART(month, T.DateVal) AS [Month],
DATEPART(year, T.DateVal) AS [Year]
FROM
(
SELECT
DATEADD(day, SEQ.SeqValue, #dtStartDate) DateVal
FROM
(
SELECT
(HUNDREDS.SeqValue + TENS.SeqValue + ONES.SeqValue) SeqValue
FROM
(
SELECT 0 SeqValue
UNION ALL
SELECT 1 SeqValue
UNION ALL
SELECT 2 SeqValue
UNION ALL
SELECT 3 SeqValue
UNION ALL
SELECT 4 SeqValue
UNION ALL
SELECT 5 SeqValue
UNION ALL
SELECT 6 SeqValue
UNION ALL
SELECT 7 SeqValue
UNION ALL
SELECT 8 SeqValue
UNION ALL
SELECT 9 SeqValue
) ONES
CROSS JOIN
(
SELECT 0 SeqValue
UNION ALL
SELECT 10 SeqValue
UNION ALL
SELECT 20 SeqValue
UNION ALL
SELECT 30 SeqValue
UNION ALL
SELECT 40 SeqValue
UNION ALL
SELECT 50 SeqValue
UNION ALL
SELECT 60 SeqValue
UNION ALL
SELECT 70 SeqValue
UNION ALL
SELECT 80 SeqValue
UNION ALL
SELECT 90 SeqValue
) TENS
CROSS JOIN
(
SELECT 0 SeqValue
UNION ALL
SELECT 100 SeqValue
UNION ALL
SELECT 200 SeqValue
UNION ALL
SELECT 300 SeqValue
UNION ALL
SELECT 400 SeqValue
UNION ALL
SELECT 500 SeqValue
UNION ALL
SELECT 600 SeqValue
UNION ALL
SELECT 700 SeqValue
UNION ALL
SELECT 800 SeqValue
UNION ALL
SELECT 900 SeqValue
) HUNDREDS
) SEQ
) T
WHERE
T.DateVal <= #dtEndDate
ORDER BY
T.DateVal ASC
Edited to add beginning blank days. Same logic can be used to append blank days if necessary.
declare #begindate date = '5-1-2015'
declare #enddate date = '6-1-2015'
create table MyCal (MyWeekday varchar(10), MyDate varchar(2), MyMonth varchar(10), MyYear int)
declare #DaysFromMonday int =
case datepart(weekday, #begindate)
when 1 then 6
else datepart(weekday, #begindate) - 2
end
declare #datecounter date = dateadd(dd, -1* #daysFromMonday, #begindate)
while #datecounter < #enddate
begin
insert into MyCal values (
datename(weekday, #datecounter)
, case when #datecounter < #begindate then '' else cast(datepart(DD, #datecounter) as varchar) end
, datename(month, #datecounter)
, datepart(YYYY, #datecounter)
)
set #datecounter = dateadd(day, 1, #datecounter)
end
select * from MyCal
You can use this piece of code.
Replace the #tmp table with a proper table name that you prefer.
declare #start_date datetime, #end_date datetime, #cur_date datetime
set #start_date = '2015-05-01'
set #end_date = '2016-04-30'
set #cur_date = #start_date
create table #tmp
(weekday varchar(10),
date int,
month varchar(10),
year int)
while #cur_date <= #end_date
begin
insert into #tmp
select datename(dw, #cur_date), datepart(day, #cur_date), datename(month, #cur_date), datepart(year, #cur_date)
set #cur_date = dateadd(dd, 1, #cur_date)
end
select * from #tmp
drop table #tmp
Try this version - You will have to run one month at a time. I've also added the blanks if the last day of the month is not a Sunday e.g. Try he month of August 2015.
declare #start_date datetime, #end_date datetime, #cur_date datetime
set #start_date = '2015-05-01'
set #end_date = '2015-05-31'
set #cur_date = #start_date
create table #tmp
(weekday varchar(10),
date varchar(2),
month varchar(10),
year int)
while datepart(dw, #cur_date) > 2
begin
insert into #tmp
select datename(dw, dateadd(dd, -(datepart(dw, #cur_date) - 2), #start_date)), '', datename(month, #start_date), datepart(year, #start_date)
set #cur_date = dateadd(dd, -1, #cur_date)
end
set #cur_date = #start_date
while #cur_date <= #end_date
begin
insert into #tmp
select datename(dw, #cur_date), datepart(day, #cur_date), datename(month, #cur_date), datepart(year, #cur_date)
set #cur_date = dateadd(dd, 1, #cur_date)
end
set #cur_date = #end_date
while datepart(dw, #cur_date) > 1
begin
insert into #tmp
select datename(dw, dateadd(dd, 1, #cur_date)), '', datename(month, #end_date), datepart(year, #end_date)
set #cur_date = dateadd(dd, 1, #cur_date)
end
select * from #tmp
drop table #tmp
Hope this helps.
Here is an alternative method using multiple CTE queries and SQL EMONTH end of month function with DATENAME SQL function
declare #date as date = dateadd(mm,0,getdate())
;with monthdates as (
SELECT dateadd(dd,1,EOMONTH(dateadd(mm,-1,#date))) firstofmonth, EOMONTH (#date) lastofmonth
), calc as (
select
firstofmonth, lastofmonth,
dateadd(dd,
-1 * case when datepart(dw,firstofmonth)-2 >= 0 then datepart(dw,firstofmonth)-2 else datepart(dw,firstofmonth)+7-2 end,
firstofmonth
) firstofcalendar,
datediff(WEEK,
dateadd(dd,
-1 * case when datepart(dw,firstofmonth)-2 >= 0 then datepart(dw,firstofmonth)-2 else datepart(dw,firstofmonth)+7-2 end,
firstofmonth
),
lastofmonth) weekcount
from monthdates
), calendar1 as (
select
firstofcalendar,
firstofmonth,
lastofmonth,
case when dateadd(dd, weekcount * 7 - 1, firstofcalendar) = lastofmonth then lastofmonth
else
dateadd(dd, (weekcount+1) * 7 - 1, firstofcalendar)
end as lastofcalendar
from calc
), calendar as (
select
datename(dw,date) dayname,
case when month(date) = month(#date) then cast(cast(date as date) as nvarchar) else '' end as date
from calendar1
cross apply [dbo].[DateTable](firstofcalendar, lastofcalendar)
)
select *, datename(mm,#date) month, datepart(yyyy,#date) year from calendar
Something like this should do you. It should work for any start-of-week you choose: you just need to set datefirst N on your connection/query to suit. If you want the week to start on Monday, use set datefirst 1.
Here's the query:
set datefirst = 1 -- start the week on Monday
declare #month date = '1 May 2015'
;with
month_days as
(
select dd = -5 union all
select dd = -4 union all
select dd = -3 union all
select dd = -2 union all
select dd = -1 union all
select dd = 0 union all
select dd = 1 union all
select dd = 2 union all
select dd = 3 union all
select dd = 4 union all
select dd = 5 union all
select dd = 6 union all
select dd = 7 union all
select dd = 8 union all
select dd = 9 union all
select dd = 10 union all
select dd = 11 union all
select dd = 12 union all
select dd = 13 union all
select dd = 14 union all
select dd = 15 union all
select dd = 16 union all
select dd = 17 union all
select dd = 18 union all
select dd = 19 union all
select dd = 20 union all
select dd = 21 union all
select dd = 22 union all
select dd = 23 union all
select dd = 24 union all
select dd = 25 union all
select dd = 26 union all
select dd = 27 union all
select dd = 28 union all
select dd = 29 union all
select dd = 30 union all
select dd = 31 union all
select dd = 32 union all
select dd = 33 union all
select dd = 34 union all
select dd = 35 union all
select dd = 36 union all
select dd = 37
),
calendar as
(
select * ,
yyyymmdd = dateadd(day,t.dd-1,#month)
from month_days t
)
select Day_Of_Week = datename(weekday,c.yyyymmdd ) ,
DD = datepart(day , c.yyyymmdd ) ,
MM = datepart(month , c.yyyymmdd ) ,
YYYY = datepart(year , c.yyyymmdd )
from calendar c
where datediff(month,#month,c.yyyymmdd) = 0 -- current month
OR ( datediff(month,#month,c.yyyymmdd) < 0 -- previous month
and datepart(weekday,c.yyyymmdd) < datepart(weekday,#month) ) -- ...to pad out the first week
OR ( datediff(month,#month,c.yyyymmdd) > 0 -- next month
and datepart(weekday,c.yyyymmdd) >= datepart(weekday,dateadd(month,1,#month)) -- ... to pad out the last week
)
order by c.yyyymmdd
Running the above query produces the expected
Day_Of_Week DD MM YYYY
=========== == == ====
Monday 27 4 2015
Tuesday 28 4 2015
Wednesday 29 4 2015
Thursday 30 4 2015
Friday 1 5 2015
Saturday 2 5 2015
Sunday 3 5 2015
Monday 4 5 2015
Tuesday 5 5 2015
Wednesday 6 5 2015
Thursday 7 5 2015
Friday 8 5 2015
Saturday 9 5 2015
Sunday 10 5 2015
Monday 11 5 2015
Tuesday 12 5 2015
Wednesday 13 5 2015
Thursday 14 5 2015
Friday 15 5 2015
Saturday 16 5 2015
Sunday 17 5 2015
Monday 18 5 2015
Tuesday 19 5 2015
Wednesday 20 5 2015
Thursday 21 5 2015
Friday 22 5 2015
Saturday 23 5 2015
Sunday 24 5 2015
Monday 25 5 2015
Tuesday 26 5 2015
Wednesday 27 5 2015
Thursday 28 5 2015
Friday 29 5 2015
Saturday 30 5 2015
Sunday 31 5 2015
Monday 1 6 2015
Tuesday 2 6 2015
Wednesday 3 6 2015
Thursday 4 6 2015
Friday 5 6 2015
Saturday 6 6 2015

Dynamic column name change based on date [T-SQL]

Is there a way to make this work? change static, "13MonthsAgo"
into January?
Original
> COUNT(CASE WHEN dateadd(MONTH, - 13, getdate()) >
> HireDate AND dateadd(MONTH, - 13, getdate()) <
> TerminationDate OR
>TerminationDate IS NULL THEN 1 ELSE NULL END) AS 13Monthsago
Preferred
> COUNT(CASE WHEN dateadd(MONTH, - 13, getdate()) >
> HireDate AND dateadd(MONTH, - 13, getdate()) <
> TerminationDate OR
>TerminationDate IS NULL THEN 1 ELSE NULL END) AS
>DATENAME(month, dateadd(MONTH,-13,getdate()))
A lengthy approach but will work..
declare #MonthName varchar(20)
select 1 num, 'January' name into #Months
union
select 2 num, 'February' name
union
select 3 num, 'March' name
union
select 4 num, 'April' name
union
select 5 num, 'May' name
union
select 6 num, 'June' name
union
select 7 num, 'July' name
union
select 8 num, 'August' name
union
select 9 num, 'September' name
union
select 10 num, 'October' name
union
select 11 num, 'November' name
union
select 12 num, 'December' name
select #MonthName = name from #Months where datepart(mm,getdate()) = num
--Add the other columns to the dataset here
--This is just an example
select HireDate, TerminationDate,
COUNT(CASE WHEN dateadd(MONTH, - 13, getdate()) >
HireDate AND dateadd(MONTH, - 13, getdate()) <
TerminationDate OR
TerminationDate IS NULL THEN 1 ELSE NULL END) AS 13Monthsago
into #Dataset
FROM SomeTable GROUP BY HireDate, TerminationDate
use tempdb
EXEC sp_RENAME '#Dataset.13Monthsago' , #MonthName, 'COLUMN'
SELECT * FROM #Dataset
This could be one way to go about it.
;with cte_Dates AS
(
SELECT CAST('20150101' as DATEtime) as DateStr UNION ALL
SELECT '20150201' UNION ALL
SELECT '20150202' UNION ALL
SELECT '20150203' UNION ALL
SELECT '20150204' UNION ALL
SELECT '20150301' UNION ALL
SELECT '20150401' UNION ALL
SELECT '20150501' UNION ALL
SELECT '20150601' UNION ALL
SELECT '20150701' UNION ALL
SELECT '20150801' UNION ALL
SELECT '20150901' UNION ALL
SELECT '20151001' UNION ALL
SELECT '20151101' UNION ALL
SELECT '20151201'
)
SELECT *
FROM
(SELECT DateStr,DATENAME(MONTH,DateStr) As MONTHS
FROM
cte_Dates
)P
PIVOT
(
count(DateStr)
FOR MONTHS IN ([January], [February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December])
)AS PVT

SQL How To Group by Date Ranges

Can someone help me figure out to group by a range of dates??
Right now I have query similar to this
Select date, count(x)
from data
group by date
This returns results that look like this
2011/1/1 10
2011/1/2 5
2011/1/3 8
2011/1/4 3
etc...
But I would like to count every 2 days so that the data would look like this
2011/1/1 15
2011/1/3 11
Any ideas??
Thanks
You could normalize the dates into groups of 2 by converting to a numerical integer value, and reducing to the even numbers. A simple way to do that is val / 2 * 2, because the first / 2 will be truncated of any decimal places (as long as the type of val is an integer!), and * 2 will return it to the original value except normalized to an even number. Here is an example that normalizes and groups the results using a CTE data source:
;with Data as (
select '1/1/2011' as [date], 1 as x union
select '1/1/2011' as [date], 2 as x union
select '1/1/2011' as [date], 3 as x union
select '1/1/2011' as [date], 4 as x union
select '1/1/2011' as [date], 5 as x union
select '1/1/2011' as [date], 6 as x union
select '1/1/2011' as [date], 7 as x union
select '1/1/2011' as [date], 8 as x union
select '1/1/2011' as [date], 9 as x union
select '1/1/2011' as [date], 10 as x union
select '1/2/2011' as [date], 11 as x union
select '1/2/2011' as [date], 12 as x union
select '1/2/2011' as [date], 13 as x union
select '1/2/2011' as [date], 14 as x union
select '1/2/2011' as [date], 15 as x union
select '1/3/2011' as [date], 16 as x union
select '1/3/2011' as [date], 17 as x union
select '1/3/2011' as [date], 18 as x union
select '1/3/2011' as [date], 19 as x union
select '1/3/2011' as [date], 20 as x union
select '1/3/2011' as [date], 21 as x union
select '1/3/2011' as [date], 22 as x union
select '1/3/2011' as [date], 23 as x union
select '1/4/2011' as [date], 24 as x union
select '1/4/2011' as [date], 25 as x union
select '1/4/2011' as [date], 26 as x
)
Select
cast(cast(cast(Date as datetime) as integer) / 2 * 2 as datetime) as date,
count(x)
from data
group by cast(cast(Date as datetime) as integer) / 2 * 2
Output:
date (No column name)
2011-01-01 00:00:00.000 15
2011-01-03 00:00:00.000 11
Select floor((date - trunc(date,'MM')) / 2), count(x)
from data
group by floor((date - trunc(date,'MM')) / 2)

Generate a range of dates using SQL

I have a SQL query that takes a date parameter (if I were to throw it into a function) and I need to run it on every day of the last year.
How to generate a list of the last 365 days, so I can use straight-up SQL to do this?
Obviously generating a list 0..364 would work, too, since I could always:
SELECT SYSDATE - val FROM (...);
There's no need to use extra large tables or ALL_OBJECTS table:
SELECT TRUNC (SYSDATE - ROWNUM) dt
FROM DUAL CONNECT BY ROWNUM < 366
will do the trick.
Recently I had a similar problem and solved it with this easy query:
SELECT
(to_date(:p_to_date,'DD-MM-YYYY') - level + 1) AS day
FROM
dual
CONNECT BY LEVEL <= (to_date(:p_to_date,'DD-MM-YYYY') - to_date(:p_from_date,'DD-MM-YYYY') + 1);
Example
SELECT
(to_date('01-05-2015','DD-MM-YYYY') - level + 1) AS day
FROM
dual
CONNECT BY LEVEL <= (to_date('01-05-2015','DD-MM-YYYY') - to_date('01-04-2015','DD-MM-YYYY') + 1);
Result
01-05-2015 00:00:00
30-04-2015 00:00:00
29-04-2015 00:00:00
28-04-2015 00:00:00
27-04-2015 00:00:00
26-04-2015 00:00:00
25-04-2015 00:00:00
24-04-2015 00:00:00
23-04-2015 00:00:00
22-04-2015 00:00:00
21-04-2015 00:00:00
20-04-2015 00:00:00
19-04-2015 00:00:00
18-04-2015 00:00:00
17-04-2015 00:00:00
16-04-2015 00:00:00
15-04-2015 00:00:00
14-04-2015 00:00:00
13-04-2015 00:00:00
12-04-2015 00:00:00
11-04-2015 00:00:00
10-04-2015 00:00:00
09-04-2015 00:00:00
08-04-2015 00:00:00
07-04-2015 00:00:00
06-04-2015 00:00:00
05-04-2015 00:00:00
04-04-2015 00:00:00
03-04-2015 00:00:00
02-04-2015 00:00:00
01-04-2015 00:00:00
SELECT (sysdate-365 + (LEVEL -1)) AS DATES
FROM DUAL connect by level <=( sysdate-(sysdate-365))
if a 'from' and a 'to' date is replaced in place of sysdate and sysdate-365, the output will be a range of dates between the from and to date.
A method quite frequently used in Oracle is something like this:
select trunc(sysdate)-rn
from
( select rownum rn
from dual
connect by level <= 365)
/
Personally, if an application has a need for a list of dates then I'd just create a table with them, or create a table with a series of integers up to something ridiculous like one million that can be used for this sort of thing.
Oracle specific, and doesn't rely on pre-existing large tables or complicated system views over data dictionary objects.
SELECT c1 from dual
MODEL DIMENSION BY (1 as rn) MEASURES (sysdate as c1)
RULES ITERATE (365)
(c1[ITERATION_NUMBER]=SYSDATE-ITERATION_NUMBER)
order by 1
About a year and a half too late, but for posterity here is a version for Teradata:
SELECT calendar_date
FROM SYS_CALENDAR.Calendar
WHERE SYS_CALENDAR.Calendar.calendar_date between '2010-01-01' (date) and '2010-01-03' (date)
Date range between 12/31/1996 and 12/31/2020
SELECT dt, to_char(dt, 'MM/DD/YYYY') as date_name,
EXTRACT(year from dt) as year,
EXTRACT(year from fiscal_dt) as fiscal_year,
initcap(to_char(dt, 'MON')) as month,
to_char(dt, 'YYYY') || ' ' || initcap(to_char(dt, 'MON')) as year_month,
to_char(fiscal_dt, 'YYYY') || ' ' || initcap(to_char(dt, 'MON')) as fiscal_year_month,
EXTRACT(year from dt)*100 + EXTRACT(month from dt) as year_month_id,
EXTRACT(year from fiscal_dt)*100 + EXTRACT(month from fiscal_dt) as fiscal_year_month_id,
to_char(dt, 'YYYY') || ' Q' || to_char(dt, 'Q') as quarter,
to_char(fiscal_dt, 'YYYY') || ' Q' || to_char(fiscal_dt, 'Q') as fiscal_quarter
--, EXTRACT(day from dt) as day_of_month, to_char(dt, 'YYYY-WW') as week_of_year, to_char(dt, 'D') as day_of_week
FROM (
SELECT dt, add_months(dt, 6) as fiscal_dt --starts July 1st
FROM (
SELECT TO_DATE('12/31/1996', 'mm/dd/yyyy') + ROWNUM as dt
FROM DUAL CONNECT BY ROWNUM < 366 * 30 --30 years
)
WHERE dt <= TO_DATE('12/31/2020', 'mm/dd/yyyy')
)
Ahahaha, here's a funny way I just came up with to do this:
select SYSDATE - ROWNUM
from shipment_weights sw
where ROWNUM < 365;
where shipment_weights is any large table;
I had the same requirement - I just use this. User enters the number of days by which he/she wants to limit the calendar range to.
SELECT DAY, offset
FROM (SELECT to_char(SYSDATE, 'DD-MON-YYYY') AS DAY, 0 AS offset
FROM DUAL
UNION ALL
SELECT to_char(SYSDATE - rownum, 'DD-MON-YYYY'), rownum
FROM all_objects d)
where offset <= &No_of_days
I use the above result set as driving view in LEFT OUTER JOIN with other views involving tables which have dates.
A week from 6 months back
SELECT (date'2015-08-03' + (LEVEL-1)) AS DATES
FROM DUAL
where ROWNUM < 8
connect by level <= (sysdate-date'2015-08-03');
if you omit ROWNUM you get 50 rows only, independent of the value.
Better late than never. Here's a method that I devised (after reading this post) for returning a list of dates that includes: (a) day 1 of of the current month through today, PLUS (b) all dates for the past two months:
select (sysdate +1 - rownum) dt
from dual
connect by rownum <= (sysdate - add_months(sysdate - extract(day from sysdate),-2));
The "-2" is the number of prior full months of dates to include. For example, on July 10th, this SQL returns a list of all dates from May 1 through July 10 - i.e. two full prior months plus the current partial month.
Another simple way to get 365 days from today would be:
SELECT (TRUNC(sysdate) + (LEVEL-366)) AS DATE_ID
FROM DUAL connect by level <=( (sysdate)-(sysdate-366));
For the fun of it, here's some code that should work in SQL Server, Oracle, or MySQL:
SELECT current_timestamp - CAST(d1.digit + d2.digit + d3.digit as int)
FROM
(
SELECT digit
FROM
(
select '1' as digit
union select '2'
union select '3'
union select '4'
union select '5'
union select '6'
union select '7'
union select '8'
union select '9'
union select '0'
) digits
) d1
CROSS JOIN
(
SELECT digit
FROM
(
select '1' as digit
union select '2'
union select '3'
union select '4'
union select '5'
union select '6'
union select '7'
union select '8'
union select '9'
union select '0'
) digits
) d2
CROSS JOIN
(
SELECT digit
FROM
(
select '1' as digit
union select '2'
union select '3'
union select '4'
union select '5'
union select '6'
union select '7'
union select '8'
union select '9'
union select '0'
) digits
) d3
WHERE CAST(d1.digit + d2.digit + d3.digit as int) < 365
ORDER BY d1.digit, d2.digit, d3.digit -- order not really needed here
Bonus points if you can give me a cross-platform syntax to re-use the digits table.
I do this so often for a scheduling app I work on that I created a pipelined table function. Sometimes I need days, hours or 15 minutes between times. This is not exactly the same function I use, because my code is in a package. However, here, I'm getting days between Jan 1 2020 and Jan 10 2020:
SELECT
days.date_time
FROM
table(between_times(TO_DATE('2020-01-01'),TO_DATE('2020-01-10'),(60*24), 'Y')) days
The pipelined function:
function between_times(i_start_time TIMESTAMP, i_end_time TIMESTAMP, i_interval_in_minutes NUMBER, include_end_time VARCHAR2 := 'N')
RETURN DateTableType PIPELINED
AS
time_counter TIMESTAMP := i_start_time;
BEGIN
IF i_start_time IS NULL OR i_end_time IS NULL or i_start_time > i_end_time OR i_interval_in_minutes IS NULL OR
i_interval_in_minutes <= 0 THEN
RETURN;
END IF;
LOOP
-- by default does not include end time
if (include_end_time = 'Y') THEN
exit when time_counter > i_end_time;
ELSE
exit when time_counter >= i_end_time;
END IF;
pipe row(DateType( time_counter ));
time_counter := time_counter + i_interval_in_minutes/(60*24);
END LOOP;
EXCEPTION WHEN NO_DATA_NEEDED THEN NULL;
END;
WITH Date_Table (Dates, Heading) AS -- Using Oracle SQL
(SELECT TRUNC(SYSDATE) Dates, ' Start' as Heading FROM dual
UNION ALL
SELECT TRUNC(DATES-1) , ' Inside recursion' as Heading FROM Date_Table
WHERE Dates > sysdate-365 ) -- Go back one year
SELECT TO_CHAR(Dates,'MM/DD/YYYY')
FROM Date_Table
ORDER BY Dates DESC;
I don't have the answer to re-use the digits table but here is a code sample that will work at least in SQL server and is a bit faster.
print("code sample");
select top 366 current_timestamp - row_number() over( order by l.A * r.A) as DateValue
from (
select 1 as A union
select 2 union
select 3 union
select 4 union
select 5 union
select 6 union
select 7 union
select 8 union
select 9 union
select 10 union
select 11 union
select 12 union
select 13 union
select 14 union
select 15 union
select 16 union
select 17 union
select 18 union
select 19 union
select 20 union
select 21
) l
cross join (
select 1 as A union
select 2 union
select 3 union
select 4 union
select 5 union
select 6 union
select 7 union
select 8 union
select 9 union
select 10 union
select 11 union
select 12 union
select 13 union
select 14 union
select 15 union
select 16 union
select 17 union
select 18
) r
print("code sample");
This query generates a list of dates 4000 days in the future and 5000 in the past as of today (inspired on http://blogs.x2line.com/al/articles/207.aspx):
SELECT * FROM (SELECT
(CONVERT(SMALLDATETIME, CONVERT(CHAR,GETDATE() ,103)) + 4000 -
n4.num * 1000 -
n3.num * 100 -
n2.num * 10 -
n1.num) AS Date,
year(CONVERT(SMALLDATETIME, CONVERT(CHAR,GETDATE() ,103)) + 4000 -
n4.num * 1000 -
n3.num * 100 -
n2.num * 10 -
n1.num) as Year,
month(CONVERT(SMALLDATETIME, CONVERT(CHAR,GETDATE() ,103)) + 4000 -
n4.num * 1000 -
n3.num * 100 -
n2.num * 10 -
n1.num) as Month,
day(CONVERT(SMALLDATETIME, CONVERT(CHAR,GETDATE() ,103)) + 4000 -
n4.num * 1000 -
n3.num * 100 -
n2.num * 10 -
n1.num) as Day
FROM (SELECT 0 AS num union ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9) n1
,(SELECT 0 AS num UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9) n2
,(SELECT 0 AS num union ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9) n3
,(SELECT 0 AS num UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8) n4
) GenCalendar ORDER BY 1