How to separate range of year on oracle - sql

I am working on a db oracle and I need to create a query where it return a range of date. For example:
Supose that I had a field of like this:
I need to get this dates and apply a range of years to return someting like:
|'0-5'|'6-10'|'11-15'|...
| 10 | 35 | 20 |...
where each range contains a number of people in this range of years old.
I tried to use SELECT CASE...
SELECT CASE
WHEN DATE_BORN <= DATE_BORN + 5 THEN '0 - 5
WHEN DATE_BORN >= DATE_BORN + 6 AND DATE_BORN <= 10 THEN '6 - 10'
END AS AGE_RANGE,
COUNT(*)
FROM MY_TABLE
GROUP BY 1
So I saw that this way change only days not year.
How can I write this query?

That's conditional aggregation:
SQL> with test (date_born) as
2 (select date '2000-05-12' from dual union all
3 select date '2001-05-12' from dual union all
4 select date '2012-05-12' from dual union all
5 select date '2013-05-12' from dual union all
6 select date '2004-05-12' from dual union all
7 select date '2008-05-12' from dual union all
8 select date '2009-05-12' from dual union all
9 select date '2001-05-12' from dual union all
10 select date '2012-05-12' from dual union all
11 select date '2001-05-12' from dual union all
12 select date '2004-05-12' from dual union all
13 select date '2005-05-12' from dual
14 )
15 select
16 sum(case when extract (year from date_born) between 2000 and 2005 then 1 else 0 end) as "2000 - 2005",
17 sum(case when extract (year from date_born) between 2006 and 2010 then 1 else 0 end) as "2006 - 2010",
18 sum(case when extract (year from date_born) between 2011 and 2015 then 1 else 0 end) as "2011 - 2015"
19 from test;
2000 - 2005 2006 - 2010 2011 - 2015
----------- ----------- -----------
7 2 3
SQL>

Here is a dynamic way to do this (using the sample table above)
First I think it's easier to have your ranges in rows rather than columns, easier for having a variety of dates that may change.
Second your first grouping is 6 years, so I changed it to just be series of 5 years:
with test (date_born) as
(select date '2000-05-12' from dual union all
select date '2001-05-12' from dual union all
select date '2012-05-12' from dual union all
select date '2013-05-12' from dual union all
select date '2004-05-12' from dual union all
select date '2008-05-12' from dual union all
select date '2009-05-12' from dual union all
select date '2001-05-12' from dual union all
select date '2012-05-12' from dual union all
select date '2001-05-12' from dual union all
select date '2004-05-12' from dual union all
select date '2005-05-12' from dual
)
,mydata AS (
SELECT
(SELECT min(extract(YEAR FROM date_born)) FROM test)+((LEVEL-1)*5)dt1
,(SELECT min(extract(YEAR FROM date_born)) FROM test)+((LEVEL-1)*5)+4 dt2
FROM dual CONNECT BY LEVEL*5 <=
(SELECT max(extract(YEAR FROM date_born))-min(extract(YEAR FROM date_born)) FROM test)+5)
SELECT d.*, count(t.date_born) cnt FROM mydata d
LEFT JOIN test t ON extract(YEAR FROM date_born) BETWEEN d.dt1 AND d.dt2
GROUP BY dt1, dt2
ORDER BY dt1;
You get this for your solution
DT1 DT2 CNT
2000 2004 6
2005 2009 3
2010 2014 3
Solution is basically extracting years from dates, finding min/max of this data set, using connect to get all years in between, and then joining to count your matching records

Related

Return Month wise count if no data for month return 0 as count in oracle sql

I have a table having data for January to March (till current month) and I am able to take the month wise count.But user required is to display zero for rest of the month.Kindly suggest.
For example:
select count(a.emp_id) as cnt ,to_char(a.due_date,'MONTH') as Process_Month from EMP_Request a
where a.due_date is not null
group by to_char(a.due_date,'MONTH')
Output:
cnt Process_month
20 JANUARY
35 FEBUARY
26 March
Desired output:
cnt Process_month
20 JANUARY
35 FEBUARY
26 March
0 APRIL
0 MAY
…….
….
….
0 DECEMBER
Please assist.
use WWV_FLOW_MONTHS_MONTH to get all the month and left join with your query to get the month name from the date column and join with it
with cte
(
SELECT month_display as month FROM WWV_FLOW_MONTHS_MONTH
) , cnt as
(
select count(a.emp_id) as cnt ,
to_char(a.due_date,'MONTH') as Process_Month from EMP_Request a
where a.due_date is not null
group by to_char(a.due_date,'MONTH')
) select coalesce(Process_Month,month), cnt from cte left join cnt on cte.month=cnt.to_char(to_date(Process_Month, 'DD-MM-YYYY'), 'Month')
Right join months generator with your query:
select to_char(to_date(mth_num, 'MM'), 'MONTH') month, nvl(cnt, 0) cnt
from (
select count(emp_id) as cnt, to_char(due_date, 'mm') mth_num
from emp_request where due_date is not null
group by to_char(due_date, 'mm')) e
right join (
select to_char(level, 'fm00') mth_num
from dual connect by level <= 12) m using (mth_num)
order by mth_num
dbfiddle demo
Months generator is a simple hierarchical query which gives us 12 values 01, 02... 12:
select to_char(level, 'fm00') mth_num from dual connect by level <= 12
You can also use system views to get these numbers:
select to_char(rownum, 'fm00') mth_num from all_objects where rownum <= 12
or this syntax:
select to_char(column_value, 'fm00') mth_num
from table(sys.odcivarchar2list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
It's better to work on numbers which you can sort properly and convert to month names in the last step. This way you have natural months order.
If you want to be sure that month names are always in english, not dependent from local settings then use to_date with third parameter, like here:
select to_char(sysdate, 'month', 'nls_date_language=english') from dual
This is a general problem which is not really a sql problem. SQL doesn't really know about what months you are interested in. So the solution is to tell it in a sub query.
Here is a solution that doesn't use external tables. You simply select all months of the year and outer join your data.
select TO_CHAR(TO_DATE(available_months.m,'MM'),'MONTH') , NVL(sum(data.cnt),0) from
(select to_number(to_char(sysdate,'MM')) m, 7 cnt from dual) data,
(select 1 m from dual union select 2 from dual union select 3 from dual union select 4 from dual
union select 5 from dual union select 6 from dual union select 7 from dual
union select 8 from dual union select 9 from dual union select 10 from dual
union select 11 from dual union select 12 from dual) available_months
where
data.m (+) = available_months.m
group by available_months.m
order by available_months.m;
Or with your data query included is should look like (not tested):
select TO_CHAR(TO_DATE(available_months.m,'MM'),'MONTH') , NVL(sum(data.cnt),0) from
(select count(a.emp_id) as cnt ,to_char(a.due_date,'MONTH') as Process_Month from EMP_Request a where a.due_date is not null) data
(select 1 m from dual union select 2 from dual union select 3 from dual union select 4 from dual
union select 5 from dual union select 6 from dual union select 7 from dual
union select 8 from dual union select 9 from dual union select 10 from dual
union select 11 from dual union select 12 from dual) available_months
where
data.due_date (+) = available_months.m
group by available_months.m
order by available_months.m;

Count running total in Oracle

I want to make a query, which shows the progress of the number of users on my webpage by week.
I use following query to run the users database and get the number, grouped by a week:
SELECT TRUNC(FAB.LICENSE_DATE, 'IW'),
COUNT(DISTINCT FAB.STATEMENT_NUMBER) AS "Number of account statements"
FROM USERS FAB
GROUP BY TRUNC(FAB.LAST_UPDATED_TIME, 'IW');
This gives following output:
Date | Users
----------------------
2015/09/07 | 5
2015/09/14 | 4
2015/09/21 | 6
But this is actually not what I want to achieve. I want to have the following output:
Date | Users
----------------------
2015/09/07 | 5
2015/09/14 | 9 (5 + 4)
2015/09/21 | 15 (5 + 4 + 6)
How to modify the query so I get all the results?
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE USERS (
LICENSE_DATE,
LAST_UPDATED_TIME,
STATEMENT_NUMBER
) AS
SELECT DATE '2015-09-07', DATE '2015-09-07', 1 FROM DUAL
UNION ALL SELECT DATE '2015-09-08', DATE '2015-09-08', 2 FROM DUAL
UNION ALL SELECT DATE '2015-09-08', DATE '2015-09-08', 3 FROM DUAL
UNION ALL SELECT DATE '2015-09-09', DATE '2015-09-09', 4 FROM DUAL
UNION ALL SELECT DATE '2015-09-12', DATE '2015-09-12', 5 FROM DUAL
UNION ALL SELECT DATE '2015-09-14', DATE '2015-09-15', 6 FROM DUAL
UNION ALL SELECT DATE '2015-09-15', DATE '2015-09-16', 7 FROM DUAL
UNION ALL SELECT DATE '2015-09-16', DATE '2015-09-16', 8 FROM DUAL
UNION ALL SELECT DATE '2015-09-17', DATE '2015-09-18', 9 FROM DUAL
UNION ALL SELECT DATE '2015-09-21', DATE '2015-09-21', 10 FROM DUAL
UNION ALL SELECT DATE '2015-09-21', DATE '2015-09-26', 11 FROM DUAL
UNION ALL SELECT DATE '2015-09-22', DATE '2015-09-22', 12 FROM DUAL
UNION ALL SELECT DATE '2015-09-23', DATE '2015-09-25', 13 FROM DUAL
UNION ALL SELECT DATE '2015-09-24', DATE '2015-09-24', 14 FROM DUAL
UNION ALL SELECT DATE '2015-09-27', DATE '2015-09-27', 15 FROM DUAL;
Query 1:
SELECT LAST_UPDATED_WEEK,
SUM( NUM_STATEMENTS ) OVER ( ORDER BY LAST_UPDATED_WEEK ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS "Number of account statements"
FROM (
SELECT TRUNC(LAST_UPDATED_TIME, 'IW') AS LAST_UPDATED_WEEK,
COUNT(DISTINCT STATEMENT_NUMBER) AS NUM_STATEMENTS
FROM USERS
GROUP BY
TRUNC( LAST_UPDATED_TIME, 'IW')
)
Results:
| LAST_UPDATED_WEEK | Number of account statements |
|-----------------------------|------------------------------|
| September, 07 2015 00:00:00 | 5 |
| September, 14 2015 00:00:00 | 9 |
| September, 21 2015 00:00:00 | 15 |
SELECT TRUNC(FAB.LICENSE_DATE, 'IW'),
SUM(COUNT(DISTINCT FAB.STATEMENT_NUMBER)) OVER (ORDER BY TRUNC(FAB.LAST_UPDATED_TIME, 'IW')) as "Number of account statements"
FROM USERS FAB
GROUP BY TRUNC(FAB.LAST_UPDATED_TIME, 'IW');
You can use this code block for your problem :
select u.date
,(select sum(u1.users)
from users u1
where u1.ddate <= u.date) as users
from users u;
It gives this output :
07.09.2015 5
14.09.2015 9
21.09.2015 15
Good luck
Hello you can try this code too.
WITH t1 AS
( SELECT to_date('01/01/2015','mm/dd/yyyy') rn, 5 usrs FROM dual
UNION ALL
SELECT to_date('02/01/2015','mm/dd/yyyy') rn, 4 usrs FROM dual
UNION ALL
SELECT to_date('03/01/2015','mm/dd/yyyy') rn, 8 usrs FROM dual
UNION ALL
SELECT to_date('04/01/2015','mm/dd/yyyy') rn, 2 usrs FROM dual
)
SELECT rn,
usrs,
sum(usrs) over (order by rn ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) cumm_usrs
FROM t1
GROUP BY rn,
usrs;

Oracle: Identifying peak values in a time series

I have following values in a column of table. there are two columns in table. The other column is having distinct dates in descending order.
3
4
3
21
4
4
-1
3
21
-1
4
4
8
3
3
-1
21
-1
4
The graph will be
I need only peaks higlighted in graph with circles in output
4
21
21
8
21
4
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TEST ( datetime, value ) AS
SELECT DATE '2015-01-01', 3 FROM DUAL
UNION ALL SELECT DATE '2015-01-02', 4 FROM DUAL
UNION ALL SELECT DATE '2015-01-03', 3 FROM DUAL
UNION ALL SELECT DATE '2015-01-04', 21 FROM DUAL
UNION ALL SELECT DATE '2015-01-05', 4 FROM DUAL
UNION ALL SELECT DATE '2015-01-06', 4 FROM DUAL
UNION ALL SELECT DATE '2015-01-07', -1 FROM DUAL
UNION ALL SELECT DATE '2015-01-08', 3 FROM DUAL
UNION ALL SELECT DATE '2015-01-09', 21 FROM DUAL
UNION ALL SELECT DATE '2015-01-10', -1 FROM DUAL
UNION ALL SELECT DATE '2015-01-11', 4 FROM DUAL
UNION ALL SELECT DATE '2015-01-12', 4 FROM DUAL
UNION ALL SELECT DATE '2015-01-13', 8 FROM DUAL
UNION ALL SELECT DATE '2015-01-14', 3 FROM DUAL
UNION ALL SELECT DATE '2015-01-15', 3 FROM DUAL
UNION ALL SELECT DATE '2015-01-16', -1 FROM DUAL
UNION ALL SELECT DATE '2015-01-17', 21 FROM DUAL
UNION ALL SELECT DATE '2015-01-18', -1 FROM DUAL
UNION ALL SELECT DATE '2015-01-19', 4 FROM DUAL
Query 1:
SELECT datetime, value
FROM (
SELECT datetime,
LAG( value ) OVER ( ORDER BY datetime ) AS prv,
value,
LEAD( value ) OVER ( ORDER BY datetime ) AS nxt
FROM test
)
WHERE ( prv IS NULL OR prv < value )
AND ( nxt IS NULL OR nxt < value )
Results:
| DATETIME | VALUE |
|---------------------------|-------|
| January, 02 2015 00:00:00 | 4 |
| January, 04 2015 00:00:00 | 21 |
| January, 09 2015 00:00:00 | 21 |
| January, 13 2015 00:00:00 | 8 |
| January, 17 2015 00:00:00 | 21 |
| January, 19 2015 00:00:00 | 4 |
So the peak is defined as the previous value and next value being less than the current value, and you can retrieve the previous an next using LAG() and LEAD() functions.
You really need some other column (e.g. my_date) to define the order of the rows, then you can:
select my_date,
value
from (select value,
lag(value ) over (order by my_date) lag_value,
lead(value) over (order by my_date) lead_value
from my_table)
where value > coalesce(lag_value , value - 1) and
value > coalesce(lead_value, value - 1);
This would not allow for a "double-peak" such as:
1,
15,
15,
4
... for which much more complex logic would be needed.
Just for completeness the row pattern matching example:
WITH source_data(datetime, value) AS (
SELECT DATE '2015-01-01', 3 FROM DUAL UNION ALL
SELECT DATE '2015-01-02', 4 FROM DUAL UNION ALL
SELECT DATE '2015-01-03', 3 FROM DUAL UNION ALL
SELECT DATE '2015-01-04', 21 FROM DUAL UNION ALL
SELECT DATE '2015-01-05', 4 FROM DUAL UNION ALL
SELECT DATE '2015-01-06', 4 FROM DUAL UNION ALL
SELECT DATE '2015-01-07', -1 FROM DUAL UNION ALL
SELECT DATE '2015-01-08', 3 FROM DUAL UNION ALL
SELECT DATE '2015-01-09', 21 FROM DUAL UNION ALL
SELECT DATE '2015-01-10', -1 FROM DUAL UNION ALL
SELECT DATE '2015-01-11', 4 FROM DUAL UNION ALL
SELECT DATE '2015-01-12', 4 FROM DUAL UNION ALL
SELECT DATE '2015-01-13', 8 FROM DUAL UNION ALL
SELECT DATE '2015-01-14', 3 FROM DUAL UNION ALL
SELECT DATE '2015-01-15', 3 FROM DUAL UNION ALL
SELECT DATE '2015-01-16', -1 FROM DUAL UNION ALL
SELECT DATE '2015-01-17', 21 FROM DUAL UNION ALL
SELECT DATE '2015-01-18', -1 FROM DUAL UNION ALL
SELECT DATE '2015-01-19', 4 FROM DUAL
)
SELECT *
FROM
source_data MATCH_RECOGNIZE (
ORDER BY datetime
MEASURES
LAST(UP.datetime) AS datetime,
LAST(UP.value) AS value
ONE ROW PER MATCH
PATTERN ((UP DOWN) | UP$)
DEFINE
DOWN AS DOWN.value < PREV(DOWN.value),
UP AS UP.value > PREV(UP.value)
)
ORDER BY
datetime
There is a much more sophisticated method available in Oracle 12c, which is to use pattern matching SQL.
http://docs.oracle.com/database/121/DWHSG/pattern.htm#DWHSG8966
It would be overkill for a situation like this, but if you needed more complex patterns matched, such as W shaped patterns, then it would be worth investigating.
Using LAG function you can compare values from different rows. I assume the resultset you showed is ordered by another column named position.
select value
from
(select value,
lag(value,-1) over (order by position) prev,
lag(value,1) over (order by position) next
from table)
where value > prev
and value > next

Generate date range based on the date

Let say i have this table
Date Inventory Sold
---------- --------------
14/04/2014 9
15/04/2014 21
16/04/2014 10
17/04/2014 20
18/04/2014 12
19/04/2014 25
20/04/2014 33
--and many more dates
how do i make it to a table like this
Date Range Inventory Sold
-------------- ----------------
xxx-xxx 50
xxx-xxx 44
the Date Range is suppose to be the range by week, eg: 14/04/2014 - 20/04/2014
Group your data by the date truncated to "whole weeks", like for example:
with data as (
select date '2014-04-14' sold_date, 9 inventory_sold from dual
union all
select date '2014-04-15', 21 from dual
union all
select date '2014-04-16', 10 from dual
union all
select date '2014-04-17', 20 from dual
union all
select date '2014-04-18', 12 from dual
union all
select date '2014-04-19', 25 from dual
union all
select date '2014-04-20', 33 from dual
union all
select date '2014-04-21', 1 from dual
)
select trunc(sold_date,'IW') week_start
, sum(inventory_sold)
from data
group by trunc(sold_date,'IW')
order by trunc(sold_date,'IW')
IW is ISO Week, meaning it uses monday-sunday weeks. Trunc(date,'IW') gives the monday of the week.
If you need to display the range, then you can use something like:
with data as (
select date '2014-04-14' sold_date, 9 inventory_sold from dual
union all
select date '2014-04-15', 21 from dual
union all
select date '2014-04-16', 10 from dual
union all
select date '2014-04-17', 20 from dual
union all
select date '2014-04-18', 12 from dual
union all
select date '2014-04-19', 25 from dual
union all
select date '2014-04-20', 33 from dual
union all
select date '2014-04-21', 1 from dual
)
select to_char(trunc(sold_date,'W'),'DD-MM-YY')
||'->'||
to_char(trunc(sold_date,'W')+6,'DD-MM-YY') week
, sum(inventory_sold)
from data
group by trunc(sold_date,'W')
order by trunc(sold_date,'W')
Hope this is useful :-)
Pointing you to:
WHERE
date >= 'selected_date_low'
AND date <= '$selected_date_high'

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