sql: generate additional dates depending on the period - sql

Got data like this:
date_start
date_end
rate
01.04.2022
20.04.2022
1
21.04.2022
11.05.2022
7
12.05.2022
15.07.2022
5
And i need get separete dates between start\end month, like this:
date_start
date_end
rate
01.04.2022
20.04.2022
1
21.04.2022
30.04.2022
7
01.05.2022
11.05.2022
7
12.05.2022
31.05.2022
5
01.06.2022
30.06.2022
5
01.07.2022
15.07.2022
5
any ideas?

found a solution, everything works correctly on different arrays of dates, but it seems to me that there may be a much simpler and more concise solution for this case?
code below:
select d_surcharge_period_from,
d_surcharge_period_to,
case
when column_value = 1 then
d_surcharge_period_from
when column_value > 1 then
trunc(add_months(d_surcharge_period_from, column_value - 1), 'mm')
end as period_beg,
case
when lead(column_value) over(order by d_surcharge_period_from, column_value) = 1 then
d_surcharge_period_to
else
last_day(add_months(d_surcharge_period_from, column_value - 1))
end period_end,
n_rate_value,
column_value
from (select to_date('01.04.2022', 'dd.mm.yyyy') as d_surcharge_period_from,
to_date('20.04.2022', 'dd.mm.yyyy') as d_surcharge_period_to,
1 n_rate_value,
0 as n_used
from dual
union all
select to_date('21.04.2022', 'dd.mm.yyyy') as d_surcharge_period_from,
to_date('11.05.2022', 'dd.mm.yyyy') as d_surcharge_period_to,
7 n_rate_value,
0 as n_used
from dual
union all
select to_date('12.05.2022', 'dd.mm.yyyy') as d_surcharge_period_from,
to_date('15.07.2022', 'dd.mm.yyyy') as d_surcharge_period_to,
5 n_rate_value,
0 as n_used
from dual),
table(cast(multiset (select level
from dual
connect by add_months(trunc(d_surcharge_period_from, 'MM'), level - 1) <= d_surcharge_period_to) as sys.odcinumberlist))
order by d_surcharge_period_from,
column_value

Related

Oracle SQL how many days has passed since previous order

I have a problem how to calculate the days how many days has passed since previous order.
My code:
select
order_id,
order_date
from
oe.orders
where customer_id = 838
order by
order_date desc
The order_id and order_date are like below:
order_id = 1920 & order_date= 25-MAR-19 15.45.38.000000000
order_id = 1618 & order_date= 08-FEB-19 12.51.39.000000000
order_id = 1592 & order_date= 04-FEB-19 07.35.46.000000000
...
I am new user of sql and no idea how to do it. Thank you for your help!
If you want the differences in days (just the date part) then:
WITH
tbl AS
(
Select 1 "ID", To_Date('25-MAR-19 15.45.38', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 2 "ID", To_Date('08-FEB-19 12.51.39', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 3 "ID", To_Date('04-FEB-19 07.35.46', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 4 "ID", To_Date('28-JAN-19 12.13.10', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual
)
Select
ID "ID",
TRUNC(A_DATE, 'dd') - TRUNC(Nvl(First_Value(A_DATE) OVER (Order By ID Rows Between 1 Preceding And Current Row), A_DATE), 'dd') "DAYS_DIFF"
From
tbl
ID
DAYS_DIFF
1
0
2
-45
3
-4
4
-7
OR ...
Select
ID "ID",
TRUNC(A_DATE, 'dd') - TRUNC(Nvl(Last_Value(A_DATE) OVER (Order By ID Rows Between Current Row And 1 Following ), A_DATE), 'dd') "DAYS_DIFF"
From
tbl
Order By TRUNC(A_DATE, 'dd')
... result
ID
DAYS_DIFF
4
0
3
7
2
4
1
45
Regards
CREATE TABLE orders
(ORDER_ID, ORDER_DATE) AS
SELECT 3, TIMESTAMP'2022-10-31 09:54:48' FROM DUAL UNION ALL
SELECT 2, TIMESTAMP'2022-10-17 19:04:44' FROM DUAL UNION ALL
SELECT 1, TIMESTAMP'2022-10-08 14:44:23' FROM DUAL
SELECT order_id, order_date,
order_date - LAG(order_date) OVER (ORDER BY order_id) AS diff
FROM orders;
ORDER_ID ORDER_DATE DIFF
1 08-OCT-22 02.44.23.000000 PM -
2 17-OCT-22 07.04.44.000000 PM +000000009 04:20:21.000000000
3 31-OCT-22 09.54.48.000000 AM +000000013 14:50:04.000000000

Forming a sequence of actions from recurring events

I have a table that looks like this:
ID TARGET_ACTION TARGET_DATE
366 0 21.04.2021
186 1 03.04.2021
929 0 14.04.2021
366 1 17.04.2021
Each ID in this table can be repeated and have a different TARGET_ACTION value for a different date. I want to form a sequence of actions for each id, dividing it into weeks so that it looks like this:
ID 01.04.2021-07.04.2021 08.04.2021-14.04.2021 15.04.2021-21.04.2021
366 0 0 1
186 1 1 0
929 0 1 0
How can I do that?
You could use pivoting in this case, though for the whole year there would be 52 week columns.
First, your sample data:
WITH
a_table AS
(
Select 366 "ID", 0 "TARGET_ACTION", To_Date('21.04.2021', 'dd.mm.yyyy') "TARGET_DATE" From Dual Union All
Select 186 "ID", 1 "TARGET_ACTION", To_Date('03.04.2021', 'dd.mm.yyyy') "TARGET_DATE" From Dual Union All
Select 929 "ID", 0 "TARGET_ACTION", To_Date('14.04.2021', 'dd.mm.yyyy') "TARGET_DATE" From Dual Union All
Select 366 "ID", 1 "TARGET_ACTION", To_Date('17.04.2021', 'dd.mm.yyyy') "TARGET_DATE" From DUAL
),
Next thing to do is to join your dataset with some kind of time dimension table. It is a very good thing to have for different uses. The link to how to create one is here: http://oracleolap.blogspot.com/2011/01/script-for-time-dimension-table.html
Instead, I've created a small cte just with weeks from the question:
weeks AS
(
SELECT 14 "WEEK_NUM", To_Date('01.04.2021', 'dd.mm.yyyy') "WEEK_STARTS", To_Date('07.04.2021', 'dd.mm.yyyy') "WEEK_ENDS" From Dual Union All
SELECT 15 "WEEK_NUM", To_Date('08.04.2021', 'dd.mm.yyyy') "WEEK_STARTS", To_Date('14.04.2021', 'dd.mm.yyyy') "WEEK_ENDS" From Dual Union All
SELECT 16 "WEEK_NUM", To_Date('15.04.2021', 'dd.mm.yyyy') "WEEK_STARTS", To_Date('21.04.2021', 'dd.mm.yyyy') "WEEK_ENDS" From Dual
)
Now, if you get the weeks from your data and join them with time dimension you could pivot the resulting dataset. Here is the mainSQL with the result.
SELECT DISTINCT
ID, W14, W15, W16
FROM
(
SELECT
t.ID "ID",
t.TARGET_ACTION "TARGET_ACTION",
To_Char(t.TARGET_DATE, 'WW') "WEEK_NUM",
w.WEEK_STARTS "WEEK_STARTS",
w.WEEK_ENDS "WEEK_ENDS"
FROM
a_table t
INNER JOIN
weeks w ON(To_Char(w.WEEK_NUM) = To_Char(t.TARGET_DATE, 'WW'))
)
PIVOT
(
Count(WEEK_NUM)
FOR WEEK_NUM IN(14 "W14", 15 "W15", 16 "W16")
)
--
-- R e s u l t
--
-- ID W14 W15 W16
-- ---------- ---------- ---------- ----------
-- 929 0 1 0
-- 366 0 0 1
-- 186 1 0 0
Regards...

How to get top 10 data weekly (This week, Previous week, Last month, 2 months ago, 3 month ago)

I want to retrieve the Top 10 Products - weekly
Rank This week product Previous week rank Last month rank 2 month ago rank 3 month ago rank
1 Watch 2 3 1 4
2 Radio 3 2 4 5
3 Pen 4 5 6 7
4
5
6
7
8
9
10
Please help me with how to get this type of data from SQL queries. Thanks
Assuming you have a table:
CREATE TABLE your_table (
product VARCHAR2(20),
some_date_column DATE
)
Then you can use conditional aggregation inside the RANK analytic function:
SELECT product,
RANK() OVER (
ORDER BY COUNT(
CASE
WHEN some_date_column >= TRUNC(SYSDATE, 'IW')
AND some_date_column < TRUNC(SYSDATE, 'IW') + INTERVAL '7' DAY
THEN 1
END
) DESC
) AS rank_this_week,
RANK() OVER (
ORDER BY COUNT(
CASE
WHEN some_date_column >= TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY
AND some_date_column < TRUNC(SYSDATE, 'IW')
THEN 1
END
) DESC
) AS rank_last_week,
RANK() OVER (
ORDER BY COUNT(
CASE
WHEN some_date_column >= ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1)
AND some_date_column < TRUNC(SYSDATE, 'MM')
THEN 1
END
) DESC
) AS rank_last_month,
RANK() OVER (
ORDER BY COUNT(
CASE
WHEN some_date_column >= ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2)
AND some_date_column < ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1)
THEN 1
END
) DESC
) AS rank_two_month,
RANK() OVER (
ORDER BY COUNT(
CASE
WHEN some_date_column >= ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3)
AND some_date_column < ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2)
THEN 1
END
) DESC
) AS rank_three_month
FROM your_table
WHERE some_date_column < TRUNC(SYSDATE, 'IW') + INTERVAL '7' DAY
AND some_date_column >= ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3)
GROUP BY product
ORDER BY rank_this_week
FETCH FIRST 10 ROWS WITH TIES;
Which, for the sample data:
INSERT INTO your_table (product, some_date_column)
SELECT 'A', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 'B', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 2 UNION ALL
SELECT 'C', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 3 UNION ALL
SELECT 'D', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 4 UNION ALL
SELECT 'E', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT 'F', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 6 UNION ALL
SELECT 'G', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 7 UNION ALL
SELECT 'H', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 8 UNION ALL
SELECT 'I', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 9 UNION ALL
SELECT 'J', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 10 UNION ALL
SELECT 'K', TRUNC(SYSDATE, 'IW') FROM DUAL CONNECT BY LEVEL <= 11 UNION ALL
SELECT 'A', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 3 UNION ALL
SELECT 'B', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 3 UNION ALL
SELECT 'C', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 'D', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 2 UNION ALL
SELECT 'E', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT 'F', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 7 UNION ALL
SELECT 'G', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 6 UNION ALL
SELECT 'H', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 4 UNION ALL
SELECT 'I', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT 'J', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 3 UNION ALL
SELECT 'K', TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL CONNECT BY LEVEL <= 2
-- Etc.
(Note: this only has data for the last 2 weeks so all the other ranks will be ties.)
Outputs:
PRODUCT
RANK_THIS_WEEK
RANK_LAST_WEEK
RANK_LAST_MONTH
RANK_TWO_MONTH
RANK_THREE_MONTH
K
1
9
1
1
1
J
2
6
1
1
1
I
3
3
1
1
1
H
4
5
1
1
1
G
5
2
1
1
1
F
6
1
1
1
1
E
7
3
1
1
1
D
8
9
1
1
1
C
9
11
1
1
1
B
10
6
1
1
1
db<>fiddle here

Create list of weeks, categories into month intervals

I need to create a list of 53 weeks, categorized into months (these are retail months so they will make more sense to someone in retail).
I need the list to repeat up to 53 weeks, in intervals of 4-5-4.
See sql fiddle for example
I am wanting to write this in an SQL statement rather than having a table, and also not using a whole lot of unions. I'm sure there is a way using rownum and connect-by but I just can't think of the logic!
This is my try:
SELECT DISTINCT to_char(l_date,'WW') week_id ,
to_char(l_date, 'mm') month_id
FROM
(SELECT to_date('01/jan/2013', 'dd/mm/yyyy')+LEVEL-1 l_date
FROM dual
CONNECT BY LEVEL <= 365
ORDER BY 1
)
ORDER BY WEEK_ID;
EDITING AS PER YOUR SQL FIDDLE table:
SELECT week_id, MAX(month_id)
FROM
(SELECT DISTINCT TO_CHAR(l_date,'WW') week_id ,
TO_CHAR(l_date, 'mm') month_id
FROM
(SELECT TRUNC(SYSDATE, 'year')+LEVEL-1 l_date
FROM dual
CONNECT BY LEVEL <= 365
)
)
GROUP BY week_id
ORDER BY WEEK_ID;
I ended up using the following. Not ideal but it works:
SELECT ROWNUM AS WEEK_ID, 1 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 4
UNION ALL
SELECT ROWNUM+4 AS WEEK_ID, 2 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 5
UNION ALL
SELECT ROWNUM+9 AS WEEK_ID, 3 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 4
UNION ALL
SELECT ROWNUM+13 AS WEEK_ID, 4 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 4
UNION ALL
SELECT ROWNUM+17 AS WEEK_ID, 5 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 5
UNION ALL
SELECT ROWNUM+22 AS WEEK_ID, 6 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 4
UNION ALL
SELECT ROWNUM+26 AS WEEK_ID, 7 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 4
UNION ALL
SELECT ROWNUM+30 AS WEEK_ID, 8 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 5
UNION ALL
SELECT ROWNUM+35 AS WEEK_ID, 9 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 4
UNION ALL
SELECT ROWNUM+39 AS WEEK_ID, 10 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 4
UNION ALL
SELECT ROWNUM+43 AS WEEK_ID, 11 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 5
UNION ALL
SELECT ROWNUM+48 AS WEEK_ID, 12 AS MONTH_ID
FROM DUAL
CONNECT BY ROWNUM <= 5

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