Generate date range based on the date - sql

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'

Related

SQL Check in which months an ID appears and show the results in a single row for each ID

I have 3 month tables for January, February and March.
January table:
ID
Date
1
01/01/2022
2
01/02/2022
3
01/02/2022
1
01/10/2022
4
01/12/2022
February table:
ID
Date
1
02/01/2022
2
02/07/2022
3
02/10/2022
2
02/15/2022
5
02/17/2022
March table:
ID
Date
1
03/19/2022
2
03/11/2022
3
03/14/2022
3
03/25/2022
6
03/13/2022
Lets assume i make a UNION ALL between all three tables. Now taking March as reference point, i need to classify each ID according to the last date it appeared in the table (the same ID can be repeated in the table with the same or different date, the max date is to be used for the excercise). Now i need to check in which months the IDs appear and be able to sort them according to the month where the ID is present.
Months
ID
Last Date
Jan-Feb-Mar
1
03/19/12
Jan-Feb-Mar
2
03/11/12
Jan-Feb-Mar
3
03/14/12
Jan
4
01/12/2022
Feb
5
02/17/2022
Mar
6
03/13/2022
Also i would need to classify according to a range of days since the last date an ID appeared. The range of days are the following: 1-30, 31-60 and 61-90. It should look something like this table below, even better if the field Months from the previous example shown is added too
Range of Days
ID
Last Date
Months
1-30
1
03/19/12
Jan-Feb-Mar
1-30
2
03/11/12
Jan-Feb-Mar
1-30
3
03/14/12
Jan-Feb-Mar
61-90
4
01/12/2022
Jan
31-60
5
02/17/2022
Feb
1-30
6
03/13/2022
Mar
Try this...
WITH
t_jan AS
(
Select 1 "ID", To_Date('01/01/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 2 "ID", To_Date('01/02/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 3 "ID", To_Date('01/02/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 1 "ID", To_Date('01/10/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 4 "ID", To_Date('01/12/2022', 'mm/dd/yyyy') "DT" From DUAL
),
t_feb AS
(
Select 1 "ID", To_Date('02/01/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 2 "ID", To_Date('02/07/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 3 "ID", To_Date('02/10/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 2 "ID", To_Date('02/15/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 5 "ID", To_Date('02/17/2022', 'mm/dd/yyyy') "DT" From DUAL
),
t_mar AS
(
Select 1 "ID", To_Date('03/19/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 2 "ID", To_Date('03/11/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 3 "ID", To_Date('03/14/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 3 "ID", To_Date('03/25/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 6 "ID", To_Date('03/13/2022', 'mm/dd/yyyy') "DT" From DUAL
),
-- -- -- -- -- -- -- -- -- -- -- --
mnths AS
(
Select
LISTAGG(MONTHS, '-') WITHIN GROUP (ORDER BY To_Char(DT, 'mm')) "MONTHS",
ID "ID",
Max(DT) "DT"
From
(
Select To_Char(Max(DT), 'Mon') "MONTHS", ID, Max(DT) "DT" From t_jan Group By ID UNION ALL
Select To_Char(Max(DT), 'Mon') "MONTHS", ID, Max(DT) "DT" From t_feb Group By ID UNION ALL
Select To_Char(Max(DT), 'Mon') "MONTHS", ID, Max(DT) "DT" From t_mar Group By ID
)
Group By
ID
),
days AS
(
Select
m.MONTHS "MONTHS",
m.ID "ID",
m.DT "DT",
Max(LAST_DAY(m.DT)) OVER() "LAST_DT",
Max(LAST_DAY(m.DT)) OVER() - m.DT "DAYS"
From
mnths m
)
SELECT
CASE
WHEN d.DAYS Between 1 And 30 THEN '1-30'
WHEN d.DAYS Between 31 And 60 THEN '31-60'
WHEN d.DAYS Between 61 And 90 THEN '61-90'
ELSE
'???'
END "RANGE_OF_DAYS",
d.ID "ID",
d.DT "LAST_DATE",
d.MONTHS "MONTHS"
FROM
days d
ORDER BY
d.ID
--
-- Result
--
-- RANGE_OF_DAYS ID LAST_DATE MONTHS
-- 1-30 1 19-MAR-22 Jan-Feb-Mar
-- 1-30 2 11-MAR-22 Jan-Feb-Mar
-- 1-30 3 25-MAR-22 Jan-Feb-Mar
-- 61-90 4 12-JAN-22 Jan
-- 31-60 5 17-FEB-22 Feb
-- 1-30 6 13-MAR-22 Mar

How to separate range of year on oracle

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

Oracle : Get average count for last 30 business days

Oracle version 11g.
My table has records similar to these.
calendar_date ID record_count
25-OCT-2017 1 20
25-OCT-2017 2 40
25-OCT-2017 3 60
24-OCT-2017 1 70
24-OCT-2017 2 50
24-OCT-2017 3 10
20-OCT-2017 1 35
20-OCT-2017 2 60
20-OCT-2017 3 90
18-OCT-2017 1 80
18-OCT-2017 2 50
18-OCT-2017 3 45
i.e for each ID, there is one record count for a given calendar day. The days are NOT continuous, i.e there may be missing records for weekends/holidays etc. On such days, there will not be records available for any ID. However on working days there are entries available for each ID .
I need to get the average record count for last 30 business days for each id
I want an output like this. ( Don't go by the values. It is just a sample )
ID avg_count_last_30
1 150
2 130
3 110
I am trying to figure out the most efficient way to do this. I thought of using RANGE BETWEEN , ROWS BETWEEN etc , but unsure it would work.
Off course a query like this won't help as there are holidays in between.
select id, AVG(record_count) FROM mytable
where calendar_date between SYSDATE - 30 and SYSDATE - 1
group by id;
what I need is something like
select id , AVG(record_count) FROM mytable
where calendar_date between last_30th_business_day and last_business_day
group by id;
last_30th_business_day will be count(DISTINCT business_days ) starting from most recent business day going backwards till I count 30.
last_business_day will be most recent business day
Would like to know experts opinion on this and best approach.
Based on your comment try this one:
WITH mytable (calendar_date, ID, record_count) AS (
SELECT TO_DATE('25-10-2017', 'DD-MM-YYYY'), 1, 20 FROM dual UNION ALL
SELECT TO_DATE('25-10-2017', 'DD-MM-YYYY'), 2, 40 FROM dual UNION ALL
SELECT TO_DATE('25-10-2017', 'DD-MM-YYYY'), 3, 60 FROM dual UNION ALL
SELECT TO_DATE('24-10-2017', 'DD-MM-YYYY'), 1, 70 FROM dual UNION ALL
SELECT TO_DATE('24-10-2017', 'DD-MM-YYYY'), 2, 50 FROM dual UNION ALL
SELECT TO_DATE('24-10-2017', 'DD-MM-YYYY'), 3, 10 FROM dual UNION ALL
SELECT TO_DATE('20-10-2017', 'DD-MM-YYYY'), 1, 35 FROM dual UNION ALL
SELECT TO_DATE('20-10-2017', 'DD-MM-YYYY'), 2, 60 FROM dual UNION ALL
SELECT TO_DATE('20-10-2017', 'DD-MM-YYYY'), 3, 90 FROM dual UNION ALL
SELECT TO_DATE('18-10-2017', 'DD-MM-YYYY'), 1, 80 FROM dual UNION ALL
SELECT TO_DATE('18-10-2017', 'DD-MM-YYYY'), 2, 50 FROM dual UNION ALL
SELECT TO_DATE('18-10-2017', 'DD-MM-YYYY'), 3, 45 FROM dual),
t AS (
SELECT calendar_date, ID, record_count,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY calendar_date desc) AS RN
FROM mytable)
SELECT ID, AVG(RECORD_COUNT)
FROM t
WHERE rn <= 30
group by ID;

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