Oracle - get previous, current and next year from query [closed] - sql

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
Is below example good solution for get previous, current and next year?
with cte as(
Select extract(year from sysdate - 365) as year from dual
union all
Select extract(year from sysdate) as year from dual
union all
Select extract(year from sysdate + 365) as year from dual
)
Select * from cte
YEAR
-----
2020
2021
2022
Or any better solution?

I might overlook something but why not just this?
with cte as(
Select extract(year from sysdate) - 1 as year from dual
union all
Select extract(year from sysdate) as year from dual
union all
Select extract(year from sysdate) + 1 as year from dual
)
Select * from cte

Well, yours isn't correct for e.g. leap years on the last day of that year. Look at 2020:
SQL> select
2 extract(year from to_date('31.12.2020', 'dd.mm.yyyy') - 365) result
3 from dual;
RESULT
----------
2020
SQL>
See? Turns out that "previous" year for date 31.12.2020 is still 2020.
ADD_MONTHS is safer, I guess:
SQL> select
2 extract (year from add_months(to_date('31.12.2020', 'dd.mm.yyyy'), -12)) result
3 from dual;
RESULT
----------
2019
SQL>
SQL> select
2 extract (year from add_months(trunc(sysdate), -12)) previous,
3 extract (year from add_months(trunc(sysdate), 0)) this,
4 extract (year from add_months(trunc(sysdate), 12)) next
5 from dual;
PREVIOUS THIS NEXT
---------- ---------- ----------
2020 2021 2022
SQL>
(this, of course, doesn't require add_months, but I kept it to make query look prettier).
Or, why not simply
SQL> select this - 1 as previous,
2 this,
3 this + 1 as next
4 from (select extract(year from sysdate) as this from dual);
PREVIOUS THIS NEXT
---------- ---------- ----------
2020 2021 2022
SQL>
For 3 rows, use a CTE:
SQL> with temp (this) as
2 (select extract(year from sysdate) from dual)
3 select this - 1 as year from temp union all
4 select this from temp union all
5 select this + 1 from temp;
YEAR
----------
2020
2021
2022
SQL>

Related

How to sum (by year) the substraction of two columns showing a running sum of it in the same record [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 months ago.
Improve this question
With data like this
-- Sample data:
CREATE TABLE IN_OUT_TABLE (DATE_OF Date, INPUT_CASES Number(3), OUTPUT_CASES Number(3));
--
Insert Into IN_OUT_TABLE Values(To_Date('01-AUG-2019', 'dd-MON-yyyy'), 2, 1);
Insert Into IN_OUT_TABLE Values(To_Date('01-SEP-2019', 'dd-MON-yyyy'), 3, 1);
Insert Into IN_OUT_TABLE Values(To_Date('01-MAY-2020', 'dd-MON-yyyy'), 3, 3);
Insert Into IN_OUT_TABLE Values(To_Date('01-MAR-2020', 'dd-MON-yyyy'), 7, 2);
Insert Into IN_OUT_TABLE Values(To_Date('01-APR-2021', 'dd-MON-yyyy'), 3, 1);
Insert Into IN_OUT_TABLE Values(To_Date('01-JUL-2021', 'dd-MON-yyyy'), 4, 2);
Need to select the sum of the diferencies between input and output cases
(INPUT_CASES - OUTPUT_CASES) grouped by year of the DATE_OF.
The result should be shown as PERIOD for a particular year. Beside, there should be a CUMULATIVE column showing running sum of PERIOD values.
So,
PERIOD = INPUT_CASES - OUTPUT_CASES - this should be summed by year
CUMULATIVE = running sum of PERIOD - year by year (ordered by year)
Having that in mind is it posible to construct a select command something like this
SELECT DATE, PERIOD, SUM(PERIOD) OVER(ORDER BY DATE)
FROM ( SELECT EXTRACT(YEAR FROM DATE) as DATE, SUM(PERIOD) as PERIOD
FROM IN_OUT_TABLE
GROUP BY EXTRACT(YEAR FROM DATE))
... ... and get the expected result?
Expected result would be:
-- YEAR PERIOD CUMULATIVE
-- ---- ----------- -------------
-- 2019 3 3
-- 2020 5 8
-- 2021 4 12
Thanks, ...
Is this maybe the answer that you are looking for?...
WITH
tbl AS
(
Select To_Date('01-AUG-2019', 'dd-MON-yyyy') "DT", 2 "INP", 1 "OUTP" From DUAL UNION ALL
Select To_Date('01-SEP-2019', 'dd-MON-yyyy') "DT", 3 "INP", 1 "OUTP" From DUAL UNION ALL
Select To_Date('01-MAY-2020', 'dd-MON-yyyy') "DT", 3 "INP", 3 "OUTP" From DUAL UNION ALL
Select To_Date('01-MAR-2020', 'dd-MON-yyyy') "DT", 7 "INP", 2 "OUTP" From DUAL UNION ALL
Select To_Date('01-APR-2021', 'dd-MON-yyyy') "DT", 3 "INP", 1 "OUTP" From DUAL UNION ALL
Select To_Date('01-JUL-2021', 'dd-MON-yyyy') "DT", 4 "INP", 2 "OUTP" From DUAL
)
SELECT
YR,
PERIOD,
Sum(PERIOD) OVER(Order By YR ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "CUMUL"
FROM
(
SELECT DISTINCT
EXTRACT(YEAR FROM DT) "YR",
Sum(INP- OUTP) OVER(Partition By EXTRACT(YEAR FROM DT) Order By EXTRACT(YEAR FROM DT)) "PERIOD"
FROM
tbl
ORDER BY
EXTRACT(YEAR FROM DT)
)
--
-- R e s u l t
--
-- YR PERIOD CUMUL
-- ---------- ---------- ----------
-- 2019 3 3
-- 2020 5 8
-- 2021 4 12
Something like this:
with
tbl (date_, input, output) as (
select date '2019-08-01', 2, 1 from dual union all
select date '2019-09-01', 3, 1 from dual union all
select date '2020-05-01', 3, 3 from dual union all
select date '2020-03-01', 7, 2 from dual union all
select date '2021-04-01', 3, 1 from dual union all
select date '2021-07-01', 4, 2 from dual
)
select extract(year from trunc(date_, 'year')) as year_,
nvl(sum(input), 0) - nvl(sum(output), 0) as period,
sum(nvl(sum(input), 0) - nvl(sum(output), 0))
over (order by trunc(date_, 'year')) as cumulative
from tbl
group by trunc(date_, 'year')
order by year_
;
YEAR_ PERIOD CUMULATIVE
---------- ---------- ----------
2019 3 3
2020 5 8
2021 4 12
The with clause is for testing purposes only, it is not part of the query. To test the query on your actual data, remove the with clause, and use your actual table and column names (I used tbl for the table, and date_, input and output for the columns - note that date is a reserved keyword, which should not be used as column name).
In the query I truncate each date to the beginning of the year, and I group by the result. If there is an index on the date_ column, this may be more efficient than extracting the year with the extract function. (Worth trying both ways on your actual data.) In any case, you should need only one pass over the base data.
I wrapped the sums of input and output within nvl(..., 0) - that way if in a year all inputs (or all outputs) are null, the sum will be treated as 0, instead of messing up the difference (the period result).

Need SQL to generate multiple rows from subquery

I have a query that selects a month and year from a complex heirachy of tables. To simplify this question, we can use this query:
select 2 as month, 2018 as year from dual;
I need SQL that will use that query as a subquery to output 3 row: that month as well as with the preceeding 2 months (including years). So the output needed for that specific case would be:
Month Year
2 2018
1 2018
12 2017
I have no idea how to proceed. Ideas anyone?
If you have the month/year already in a DATE column, it would be one less conversion, but if they are separate columns like you have provided, you can use a query like the one below.
Query
WITH sample_data AS (SELECT 2 AS month, 2018 AS year FROM DUAL)
SELECT TO_CHAR (ADD_MONTHS (TO_DATE (month || '-' || year, 'MM-YYYY'), ((LEVEL * -1) + 1)), 'MM')
AS month,
TO_CHAR (ADD_MONTHS (TO_DATE (month || '-' || year, 'MM-YYYY'), ((LEVEL * -1) + 1)), 'YYYY')
AS year
FROM sample_data
CONNECT BY LEVEL <= 3;
Result
MONTH YEAR
________ _______
02 2018
01 2018
12 2017
For Oracle 12+:
select *
from
(select 2 as month, 2018 as year
from dual) complex_query
,lateral(
select
extract(month from add_months(dt, 1-level)) as month,
extract(year from add_months(dt, 1-level)) as year
from (
select
to_date(complex_query.year||'-'||complex_query.month||'-01', 'yyyy-mm-dd') as dt
from dual
)
connect by level<=3
);
For previous versions:
select
complex_query.*
,extract(year from (to_date(year||'-'||month,'yyyy-mm')-delta)) year_2
,extract(month from (to_date(year||'-'||month,'yyyy-mm')-delta)) month_2
from
(select 2 as month, 2018 as year
from dual) complex_query
,(select
NUMTOYMINTERVAL(level-1,'month') delta
from dual
connect by level<=3
) v
Results:
MONTH YEAR YEAR_2 MONTH_2
---------- ---------- ---------- ----------
2 2018 2018 1
2 2018 2017 12
2 2018 2017 11
Would union work?
with primary_month as (
select 2 as month, 2018 as year from dual
)
select (case when month = 1 then 13 else month end) - 1 as month , case when month = 1 then year-1 else year end as year from primary_month
union
select month, year from primary_month
union
select month + 1 as month, year from primary_month
;
This example won't handle boundary cases like if the primary month is January, since there is no month 0, but I'm not sure if that's what you're going for and it depends on the real query and schema (e.g. are you using timestamp columns?)

Problem with getting the quarter from a date in Oracle

I've written a query to get the start date of the quarters from current year and previous year by using the sysdate.
eg. Today falls in the 1st quarter of the year, therefore I only want to get the start date of 1st quarter of last year and this year.
If I'm on December (which is in the 4th quarter), I want to get the start dates of 8 quarters (4 from last year, 4 from this year.)
select b.dt,
to_number(to_char(SYSDATE, 'Q')),
to_number(to_char(b.dt, 'Q'))
from dual a,
(select add_months(trunc(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -12),
'yyyy'),
(rownum - 1) * 3) dt
from all_objects
where rownum <= 8
and add_months(trunc(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -12),
'yyyy'),
(rownum - 1) * 3) <= SYSDATE
and to_number(to_char(SYSDATE, 'Q')) >=
to_number(to_char(add_months(trunc(ADD_MONTHS(TRUNC(SYSDATE,
'MM'),
-12),
'yyyy'),
(rownum - 1) * 3),
'Q'))) b
This query only returns the start date of 1st quarter of last year. I expect to get the start date of the 1st quarter of this year as well.
Here's one option; see comments within the code.
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> with
2 -- this mimics SYSDATE
3 today (datum) as
4 (select date '&date_literal' from dual),
5 -- which quarter does DATUM belong to? Find 1st day in "this" and "previous" year
6 quart as
7 (select trunc(datum, 'yyyy') this,
8 trunc(add_months(datum, -12), 'yyyy') previous,
9 to_char(datum, 'q') quart from today)
10 -- the fina result
11 select add_months(this, (level - 1) * 3) result
12 from quart
13 connect by level <= quart
14 union all
15 select add_months(previous, (level - 1) * 3) result
16 from quart
17 connect by level <= quart;
Enter value for date_literal: 2019-03-24
RESULT
----------
01.01.2019
01.01.2018
SQL> /
Enter value for date_literal: 2019-08-13
RESULT
----------
01.01.2019
01.04.2019
01.07.2019
01.01.2018
01.04.2018
01.07.2018
6 rows selected.
SQL>

Date and quarter SQL ORACLE

I am currently working on oracle sql and have some issues managing dates and quarters.
The first thing I try to do is given a quarter and a year I would like to know the number of days of the quarter. For example, quarter 1 in 2013 had 90 days but in 2012 had 91 days.
The second thing I would like to do is convert a date dd/qq/yyyy to a date
dd/mm/yyyy.
For instance, 60/2/2013 gives 30/5/2013. I am a beginner in Oracle so any help or function names will be highly appreciated.
Thanks
this gives the number of days in the current quarter, playing around with the add_months should allow you to find the length of other quarters
select (add_months(trunc(sysdate,'q'),3) - 1) - trunc(sysdate,'q')
from dual;
For your first question, hopefully this will give you some idea of what to do:
with sample_data as (select 2012 yr, 1 quarter from dual union all
select 2012 yr, 2 quarter from dual union all
select 2012 yr, 3 quarter from dual union all
select 2012 yr, 4 quarter from dual union all
select 2013 yr, 1 quarter from dual union all
select 2013 yr, 2 quarter from dual)
---- end of mimicking a table called "sample_data"; see query below:
select yr,
quarter,
add_months(to_date('01/01/'||yr, 'dd/mm/yyyy'), (quarter - 1)*3) qtr_st,
add_months(to_date('01/01/'||yr, 'dd/mm/yyyy'), quarter * 3) - 1 qtr_end,
add_months(to_date('01/01/'||yr, 'dd/mm/yyyy'), quarter * 3) - add_months(to_date('01/01/'||yr, 'dd/mm/yyyy'), (quarter - 1)*3) diff
from sample_data;
YR QUARTER QTR_ST QTR_END DIFF
---------- ---------- ---------- ---------- ----------
2012 1 01/01/2012 31/03/2012 91
2012 2 01/04/2012 30/06/2012 91
2012 3 01/07/2012 30/09/2012 92
2012 4 01/10/2012 31/12/2012 92
2013 1 01/01/2013 31/03/2013 90
2013 2 01/04/2013 30/06/2013 91
N.B. Because you're including the day of the start_date in the count, the difference is effectively how many days between the 1st of the quarter and the 1st of the next quarter, or qtr_end - qtr_st + 1 from my query above.
For your second question, here's one way:
with sample_data as (select '60/2/2013' dy_qtr_fmt from dual union all
select '60/02/2013' dy_qtr_fmt from dual union all
select '01/1/2013' dy_qtr_fmt from dual union all
select '1/1/2013' dy_qtr_fmt from dual)
---- end of mimicking a table called "sample_data"; see query below:
select dy_qtr_fmt,
add_months(year_st, (qtr-1)*3) + num_days_in_qtr - 1 dt
from (select dy_qtr_fmt,
to_date('01/01/'||substr(dy_qtr_fmt, -4), 'dd/mm/yyyy') year_st,
to_number(substr(dy_qtr_fmt, instr(dy_qtr_fmt, '/', 1, 1) + 1, instr(dy_qtr_fmt, '/', 1, 2) - instr(dy_qtr_fmt, '/', 1, 1) -1)) qtr,
to_number(substr(dy_qtr_fmt, 1, instr(dy_qtr_fmt, '/', 1, 1) - 1)) num_days_in_qtr
from sample_data);
DY_QTR_FMT DT
---------- ----------
60/2/2013 30/05/2013
60/02/2013 30/05/2013
01/1/2013 01/01/2013
1/1/2013 01/01/2013

List all the months using oracle sql

Guys, is there any better way to list all the months other than this:
select to_char(add_months(to_date('01/01/1000', 'DD/MM/RRRR'), ind.l-1), 'MONTH') as month_descr,
ind.l as month_ind
from dual descr,
(select l
from (select level l
from dual
connect by level <= 12
)
) ind
order by 2;
ANSWER:
SELECT to_char(add_months(SYSDATE, (LEVEL-1 )),'MONTH') as months
FROM dual
CONNECT BY LEVEL <= 1
ONE MORE QUESTION SEE BELOW
Also I want to list the previous two years including the current year. I wrote this sql query. Let me know if there is anything better.
select extract(year from sysdate) - (level-1) as years
from dual
connect by level <=3
order by years
Not better, but just a bit cleaner:
SQL> select to_char(date '2000-12-01' + numtoyminterval(level,'month'),'MONTH') as month
2 from dual
3 connect by level <= 12
4 /
MONTH
---------
JANUARY
FEBRUARY
MARCH
APRIL
MAY
JUNE
JULY
AUGUST
SEPTEMBER
OCTOBER
NOVEMBER
DECEMBER
12 rows selected.
Regards,
Rob.
Yup.
1:
SELECT * FROM WWV_FLOW_MONTHS_MONTH;
2: (UPD:)
WITH MONTH_COUNTER AS (
SELECT LEVEL-1 AS ID
FROM DUAL
CONNECT BY LEVEL <= 12
)
SELECT TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1000', 'DD/MM/RRRR'), ID),'MONTH') FROM MONTH_COUNTER;
select to_char(add_months(trunc(sysdate, 'yyyy'), level - 1), 'MONTH') months
from dual
connect by level <= 12;
Returns:
MONTHS
--------------------
JANUARY
FEBRUARY
MARCH
APRIL
MAY
JUNE
JULY
AUGUST
SEPTEMBER
OCTOBER
NOVEMBER
DECEMBER
12 rows selected.
SELECT to_char(to_date( level,'mm'), 'MONTH') Months FROM DUAL CONNECT BY LEVEL <=12;
Regards,
Prasant Sutaria
SELECT TO_CHAR(TO_DATE(rownum||'-'||rownum||'-'||'2013', 'DD-MM-YYYY'), 'Month')
FROM all_objects
WHERE rownum < 13