Select query that creates a table of monthly incremental entries - sql

I need to be able to use a select query that will somehow generate a list of entries consisting of monthly increments from two variables STARTDATE and ENDDATE. An example would look like this:
Getting the STARTDATE and ENDDATE from the table
STARTDATE ENDDATE
----------- -----------
01-JAN-2011 1-DEC-2011
which results into
CALENDAR
---------
01-JAN-11
01-FEB-11
01-MAR-11
01-APR-11
01-MAY-11
01-JUN-11
01-JUL-11
01-AUG-11
01-SEP-11
01-OCT-11
01-NOV-11
01-DEC-11
Any ideas on how to doing this? Someone told me about a method called 'CONNECT BY' but it doesn't work with leap year or something.
Thanks.

Something like this should work
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select date '2011-01-01' start_date,
3 date '2011-12-01' end_date
4 from dual
5 )
6 select add_months( start_date, level-1 )
7 from x
8* connect by level <= months_between( end_date, start_date ) + 1
SQL> /
ADD_MONTH
---------
01-JAN-11
01-FEB-11
01-MAR-11
01-APR-11
01-MAY-11
01-JUN-11
01-JUL-11
01-AUG-11
01-SEP-11
01-OCT-11
01-NOV-11
01-DEC-11
12 rows selected.

Related

sql query to get the salary from past 3 years

I have a table salary that has salary details for an employee for various years like:
person_number date_from date_to salary RN
---------------------------------------------------------------------------
272 03-Mar-2022 31-dec-4712 109000 1
272 05-Mar-2021 02-Mar-2022 100000 1
272 10-Mar-2020 04-Mar-2020 100000 1
10 10-Mar-2019 31-dec-4712 4678 1
I want to get the latest salary for past 2 years along with current year. I created the below query for the same -
SELECT *
FROM
(SELECT
person_number
sal.salary_amount,
ROW_NUMBER() OVER (PARTITION BY person_number
ORDER BY date_to DESC) rn,
sal.date_from
FROM
cmp_salary sal
WHERE
1 = 1
AND TO_CHAR(sal.date_from, 'YYYY') = (SELECT TO_CHAR(Add_months(SYSDATE, -12), 'yyyy')
FROM dual)
)
WHERE
rn = 1
Like this one above I have created a clause for all 3 years (just replaced the 12 with 24,36).
These separate query is returning the correct data for 2 years back i.e till 2020 but the only case it is not working in when the same salary exists in 2 years.
Eg- For person #10, the salary is the same from 2019, 2021 and 2022.
Because I am using the year comparison in my above query, it will not return an output for 2020, 2021 and 2022, because the date_From is in 2019. Ideally it should give me the same salary for 2020, 2021 and 2022.
How to tweak this? I have to create 3 separate queries for this.
I don't think you need 3 separate queries; one would suffice. Also, according to data you posted, there's only one row per year per person so it is a "fixed" 3 years back. Therefore, I'd think of something like this instead:
Sample data:
SQL> WITH
2 cmp_salary (person_number,
3 date_from,
4 date_to,
5 salary_amount)
6 AS
7 (SELECT 272, DATE '2022-03-03', DATE '4712-12-31', 109000 FROM DUAL
8 UNION ALL
9 SELECT 272, DATE '2021-03-05', DATE '2022-03-02', 100000 FROM DUAL
10 UNION ALL
11 SELECT 272, DATE '2020-03-10', DATE '2020-03-04', 100000 FROM DUAL
12 UNION ALL
13 SELECT 10, DATE '2019-03-10', DATE '4712-12-31', 4678 FROM DUAL)
Query you might be interested in begins here:
14 SELECT person_number, sal.salary_amount, sal.date_from
15 FROM cmp_salary sal
16 CROSS JOIN
17 TABLE (CAST (MULTISET ( SELECT LEVEL
18 FROM DUAL
19 CONNECT BY LEVEL <= 3) AS SYS.odcinumberlist))
20 WHERE EXTRACT (YEAR FROM sal.date_from) =
21 EXTRACT (YEAR FROM SYSDATE) - COLUMN_VALUE + 1
22 ORDER BY person_number, date_from DESC;
PERSON_NUMBER SALARY_AMOUNT DATE_FROM
------------- ------------- ----------
272 109000 03.03.2022
272 100000 05.03.2021
272 100000 10.03.2020
SQL>

How do I get 1 week data from year 2018 based on 7 days based on this year in oracle?

I am trying to dynamically retrieve data based on dates. For example I want data from 7 days before today and same 7 days from 2020 dynamically.
I tried
SELECT *
FROM table_1
WHERE insert_date > TRUNC(SYSDATE) - 7 or ((insert_date< trunc(sysdate)-365) and (insert_date> trunc(sysdate)-372))
order by insert_date
The problem with this query is if I were to run this query now, this will give me correct data for 2020 and 2021. However if I were to run this same query in 2022, it will give me data from 2022 and 2021 when I want is data based on 2022 and 2020. I was hoping if someone could help me with this issue. I was able to figure out if I want to compare month to month but not week..
Thank you,
Sam
Presume that
today is (yyyy-mm-dd) 2021-04-08 which means that you'd want to return rows whose insert_date is
if you run the query "today", between
2021-04-01 and 2021-04-08
2020-04-01 and 2020-04-08
if you run the query "today next year" (i.e. 2022-04-08)
2022-04-01 and 2022-04-08
2020-04-01 and 2020-04-08
this is contents of your table:
SQL> select * from table_1 order by id;
ID INSERT_DAT Return in
---------- ----------
1 2021-04-08 -- 2021
2 2021-04-03 -- 2021
3 2021-04-02 -- 2021
4 2021-03-31
5 2020-04-05 -- 2021 and 2022
6 2020-04-04 -- 2021 and 2022
7 2020-04-12
8 2022-04-08 -- 2022
9 2022-03-30
9 rows selected.
Query; lines #4 and 5 make sure that query returns rows in year 2020:
SQL> select sysdate from dual;
SYSDATE
----------
2021-04-08
SQL> select a.*
2 from table_1 a
3 where a.insert_date between trunc(sysdate) - 7 and trunc(sysdate)
4 or a.insert_date between add_months(trunc(sysdate), -12 * (extract(year from sysdate) - 2020)) - 7
5 and add_months(trunc(sysdate), -12 * (extract(year from sysdate) - 2020))
6 order by a.insert_date desc;
ID INSERT_DAT
---------- ----------
1 2021-04-08
2 2021-04-03
3 2021-04-02
5 2020-04-05
6 2020-04-04
SQL>
Next year, on 2022-04-08, query would return
SQL> select sysdate from dual;
SYSDATE
----------
2022-04-08
SQL> select a.*
2 from table_1 a
3 where a.insert_date between trunc(sysdate) - 7 and trunc(sysdate)
4 or a.insert_date between add_months(trunc(sysdate), -12 * (extract(year from sysdate) - 2020)) - 7
5 and add_months(trunc(sysdate), -12 * (extract(year from sysdate) - 2020))
6 order by a.insert_date desc;
ID INSERT_DAT
---------- ----------
8 2022-04-08
5 2020-04-05
6 2020-04-04
SQL>
Didn't quite got the requirement. But, assuming you want to take 2020 as a standard date and get 7 days of data from today and the same 7 days from 2020.
I had used the substr to remove the year part and replace it with the standard year. As I had difficulty testing with 2022 date, took 2019 as my standard date.
select distinct cast(to_date(p.insert_date, 'DD-MON-YYYY') as timestamp) as insert_date,
cast(to_date(SYSDATE-7, 'DD-MON-YYYY') as timestamp) as tst_date
from table_1 p
where 1=1 and ( (to_date(p.insert_date, 'DD-MON-YYYY') > to_date(SYSDATE-7, 'DD-MON-YYYY') )
or (to_date(p.insert_date, 'DD-MON-YYYY')> trunc(to_date(substr(to_date(SYSDATE, 'DD-MON-YYYY'),1,7)||'19', 'DD-MON-YYYY')-7)
and to_date(p.insert_date, 'DD-MON-YYYY') <= trunc(to_date(substr(to_date(SYSDATE, 'DD-MON-YYYY'),1,7)||'19', 'DD-MON-YYYY')))
)
order by create_date desc;
-- Standard date for 2020
select substr(to_date(SYSDATE, 'DD-MON-YYYY'),1,7)||'20' from dual;

Generating Rows for each date from start date to end date [duplicate]

This question already has answers here:
Row for each date from start date to end date
(2 answers)
Generate series of months for every row in Oracle
(1 answer)
Create all months list from a date column in ORACLE SQL
(3 answers)
Closed 2 years ago.
What I'm trying to do is take a record that looks like this:
Start_DT End_DT ID
4/5/2013 10/9/2013 1
and change it to look like this:
DT ID
4/1/2013 1
5/1/2013 1
6/1/2013 1
7/1/2013 1
8/1/2013 1
9/1/2013 1
10/1/2013 1
I am having difficult time making this work. Any help would be appreciated.
Thanks
You can use a recursive CTE for this:
with dates (dte, end_mon, id) as (
select trunc(start_dt, 'MON') as dte, trunc(end_dt, 'MON') as end_mon, id
from t
union all
select dte + interval '1' month, end_mon, id
from dates
where dte < end_mon
)
select *
from dates;
Here is a db<>fiddle.
Alternatively, but similarly - row generator technique is what you're looking for.
(My date format is DD.MM.YYYY; can't tell for yours so I'm just guessing as both of your dates can be read in two ways, e.g. 4/5/2013 - is it 4th of May or 5th of April?).
SQL> with dates (start_dt, end_dt, id) as
2 (select date '2013-05-04', date '2013-09-10', 1 from dual)
3 select start_dt + level - 1 dt, id
4 from dates
5 connect by level <= end_dt - start_dt + 1
6 order by dt;
DT ID
---------- ----------
04.05.2013 1
05.05.2013 1
06.05.2013 1
07.05.2013 1
08.05.2013 1
<snip>
06.09.2013 1
07.09.2013 1
08.09.2013 1
09.09.2013 1
10.09.2013 1
130 rows selected.
SQL>
This can be achieved by using CONNECT BY to generate the months between each date.
Query
WITH
dates (start_date, end_date, id)
AS
(SELECT TO_DATE ('4/5/2013', 'MM/DD/YYYY'), TO_DATE ('10/9/2013', 'MM/DD/YYYY'), 1
FROM DUAL)
SELECT TO_CHAR (ADD_MONTHS (TRUNC (start_date, 'MM'), LEVEL - 1), 'MM/DD/YYYY') AS dt, id
FROM dates
CONNECT BY ADD_MONTHS (TRUNC (start_date, 'MM'), LEVEL - 1) <= TRUNC (end_date, 'MM');
Result
DT ID
_____________ _____
04/01/2013 1
05/01/2013 1
06/01/2013 1
07/01/2013 1
08/01/2013 1
09/01/2013 1
10/01/2013 1

How to loop in Oracle SQL

Assume we have exampleDB and select contractdate like
SELECT DB.contractdate
FROM exampleDB DB
contractdate
2014/12/1
2015/12/1
2016/12/1
2017/12/1
2018/12/1
2019/12/1
I would like to count the policy number at each time like
each time policy count
2014/1/1 0
2015/1/1 1
2016/1/1 2
2017/1/1 3
2018/1/1 4
2019/1/1 5
I tried
WHERE DB.contractdate <='2014/1/1';
But I must loop such code manually.
How can I loop?
If the binning is every month,it is very stressful process.
can they be combined into one?
Best regards
select contractdate as "each time",
count(*) as "policy count"
from exampleDB
where contractdate in (mention all dates you want)
group by contractdate
Hope this will help you.
you can use row_num() and trunc() to get 1st day of the month
SELECT TRUNC(DB.contractdate, 'MONTH'), row_number() over (order by DB.contractdate) -1 as policy_count
FROM exampleDB DB
You can use COUNT analytical function with RANGE operator as follows:
SQL> with dataa(contractdate) as
2 (
3 select date '2014-12-01' from dual union all
4 select date '2015-12-01' from dual union all
5 select date '2016-12-01' from dual union all
6 select date '2017-12-01' from dual union all
7 select date '2018-12-01' from dual union all
8 select date '2019-12-01' from dual
9 )
10 SELECT
11 TRUNC(CONTRACTDATE, 'year') as "each time",
12 COUNT(1) OVER(
13 ORDER BY
14 CONTRACTDATE DESC
15 RANGE BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING
16 ) as "policy count"
17 FROM
18 DATAA order by 1;
each time policy count
--------- ------------
01-JAN-14 0
01-JAN-15 1
01-JAN-16 2
01-JAN-17 3
01-JAN-18 4
01-JAN-19 5
6 rows selected.
SQL>
Cheers!!

oracle count based on month

I am attempting to write Oracle SQL.
I am looking for solution something similar. Please find below data I have
start_date end_date customer
01-01-2012 31-06-2012 a
01-01-2012 31-01-2012 b
01-02-2012 31-03-2012 c
I want the count of customer in that date period. My result should look like below
Month : Customer Count
JAN-12 : 2
FEB-12 : 2
MAR-12 : 2
APR-12 : 1
MAY-12 : 1
JUN-12 : 1
One option would be to generate the months separately in another query and join that to your data table (note that I'm assuming that you intended customer A to have an end-date of June 30, 2012 since there is no June 31).
SQL> ed
Wrote file afiedt.buf
1 with mnths as(
2 select add_months( date '2012-01-01', level - 1 ) mnth
3 from dual
4 connect by level <= 6 ),
5 data as (
6 select date '2012-01-01' start_date, date '2012-06-30' end_date, 'a' customer from dual union all
7 select date '2012-01-01', date '2012-01-31', 'b' from dual union all
8 select date '2012-02-01', date '2012-03-31', 'c' from dual
9 )
10 select mnths.mnth, count(*)
11 from data,
12 mnths
13 where mnths.mnth between data.start_date and data.end_date
14 group by mnths.mnth
15* order by mnths.mnth
SQL> /
MNTH COUNT(*)
--------- ----------
01-JAN-12 2
01-FEB-12 2
01-MAR-12 2
01-APR-12 1
01-MAY-12 1
01-JUN-12 1
6 rows selected.
WITH TMP(monthyear,start_date,end_date,customer) AS (
select LAST_DAY(start_date),
CAST(ADD_MONTHS(start_date, 1) AS DATE),
end_date,
customer
from data
union all
select LAST_DAY(start_date),
CAST(ADD_MONTHS(start_date, 1) AS DATE),
end_date,
customer
from TMP
where LAST_DAY(end_date) >= LAST_DAY(start_date)
)
SELECT TO_CHAR(MonthYear, 'MON-YY') TheMonth,
Count(Customer) Customers
FROM TMP
GROUP BY MonthYear
ORDER BY MonthYear;
SQLFiddle