oracle sql to postgresql level query - sql

I have an Oracle Sql query which gives a list of months in a financial year. I want to convert to postgresql as it doesn't have level
select case
when to_char(add_months(sysdate , -1), 'MM') >= 4 then
to_char(add_months(sysdate , -1), 'YYYY') || '-' ||
to_char(to_number(to_char(add_months(sysdate , -1), 'YYYY')) + 1)
else
to_char(to_number(to_char(add_months(sysdate , -1), 'YYYY')) - 1) || '-' ||
to_char(add_months(sysdate , -1), 'YYYY')
end FY,
to_char(level, '00') MNTH
from dual
connect by level <=12

select
case when date_part('month',current_date) >= 4 then
concat(date_part('year', current_date), '-', date_part('year',current_date)+1)
else
concat(date_part('year', current_date)-1, '-', date_part('year',current_date))
end FY
, lpad(date_part('month',d)::varchar,2,'0') MNTH
from (select DATE '2008-01-01' + (interval '1' month * generate_series(0,11)) d ) y
current_date (instead of sysdate)
+ Interval 'n Month' (instead of add_months)
generate series (instead of connect by )
date_part('month',... to get the month number (instead of to_char(...,'MM') )
concat() for concatenations (looks after type conversions too)
sample result:
FY MNTH
1 2017-2018 01
2 2017-2018 02
3 2017-2018 03
4 2017-2018 04
5 2017-2018 05
6 2017-2018 06
7 2017-2018 07
8 2017-2018 08
9 2017-2018 09
10 2017-2018 10
11 2017-2018 11
12 2017-2018 12

Related

How to get first date and last date of all twelve months for given year in Oracle PLSQL?

If i give input year like '2021' i need result as below
Month Start Date End Date
1 1/1/2021 31/01/2021
2 1/2/2021 28/01/2021
.
.
.
.
.
.
.
.
.
12 1/12/2021 31/12/2021
Basically, it is about the row generator technique; there are plenty of them, pick any you want. (Have a look at OraFAQ).
For example:
SQL> with mon as
2 (select add_months(trunc(to_date(&par_year, 'yyyy'), 'yyyy'), level - 1) val
3 from dual
4 connect by level <= 12
5 )
6 select to_char(val, 'mm') mon,
7 val start_date,
8 last_day(val) end_date
9 from mon
10 order by 1;
Enter value for par_year: 2021
MO START_DATE END_DATE
-- ---------- ----------
01 01/01/2021 31/01/2021
02 01/02/2021 28/02/2021
03 01/03/2021 31/03/2021
04 01/04/2021 30/04/2021
05 01/05/2021 31/05/2021
06 01/06/2021 30/06/2021
07 01/07/2021 31/07/2021
08 01/08/2021 31/08/2021
09 01/09/2021 30/09/2021
10 01/10/2021 31/10/2021
11 01/11/2021 30/11/2021
12 01/12/2021 31/12/2021
12 rows selected.
SQL>
You could also use directly the model clause for that purpose.
SELECT n
, TO_DATE(&the_year||lpad(f, 2, '0'), 'YYYYMM') start_dt
, last_day(TO_DATE(&the_year||lpad(f, 2, '0'), 'YYYYMM')) end_dt
FROM DUAL
MODEL
DIMENSION by (1 as n)
MEASURES (1 as f)
RULES (
f[FOR n FROM 1 TO 12 INCREMENT 1 ] = cv(n)
)
;
The advantage of the model clause is if you later want to get the every other month, you just need to change the increment from 1 to 2. Or if you are looking for the quarter months of the year (January, April, Jully, October), you just need to change increment from 1 to 3, and so on...
Just replace 2021 in the query for your year
with months (m) as (
select 1 from dual union all
select m + 1 from months where m < 12
)
select
to_date('2021' || '-' || to_char(m) || '-01', 'YYYY-MM-DD') as first_day,
last_day(to_date('2021' || '-' || to_char(m) || '-01', 'YYYY-MM-DD')) as last_day
from months
You can try on this db<>fiddle
Try below query
WITH cte_date as(
SELECT
LEVEL Month_No,
to_date(to_char(LEVEL||'-2021'),'MM-YYYY') Start_Date FROM dual
CONNECT BY LEVEL <=12
)
SELECT Month_No, Start_Date, LAST_DAY(Start_Date) End_Date
FROM cte_date;
I'm a fan of recursive CTEs because they are part of Standard SQL. I would phrase this as:
with months (month, startdate) as (
select 1 as month, date '2021-01-01'
from dual
union all
select month + 1, add_months(startdate, 1)
from months
where month < 12
)
select month, startdate, last_day(startdate) as enddate
from months;
If you need an input year, there are different ways to accomplish it. But a simple way is to change the second line to:
select 1 as month, to_date(:year || '0101', 'YYYYMMDD')

Get count days in current year by day name or day id

name id
----------------
Mon 1
Thu 2
Wen 3
Thr 4
Fri 5
Sat 6
San 7
How get count day where id in eg. (1,2,3,4) and year is 2021
The result should be 208.
Actually, it is 208 for year 2021.
SQL> WITH
2 year AS (SELECT &par_year year FROM DUAL),
3 calendar
4 AS
5 ( SELECT TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy') + LEVEL - 1 datum
6 FROM year y
7 CONNECT BY LEVEL <=
8 ADD_MONTHS (TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy'), 12)
9 - TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy'))
10 SELECT SUM (CASE
11 WHEN TO_NUMBER (TO_CHAR (c.datum, 'd')) IN (1,
12 2,
13 3,
14 4)
15 THEN
16 1
17 ELSE
18 0
19 END) result
20 FROM calendar c;
Enter value for par_year: 2021
RESULT
----------
208
SQL> /
Enter value for par_year: 2020
RESULT
----------
210
SQL>
What does it do?
YEAR CTE contains year you're interested in
CALENDAR CTE creates all dates in that particular year
SUM function conditionally adds 1 if TO_CHAR(datum, 'd') is 1, 2, 3 or 4
that's all

Assign year to months of past 6 months

From below data, how do i assign year in column DS_YEAR where Year is not assigned based on DS_MONTH.
DS_MONTH| DS_DAY| DS_YEAR
--------|---------|--------
Mar | 2 | 2019
Jan | 4 | 2020
Apr | 2 | 07:43
Sep | 1 | 06:00
Jul | 2 | 05:00
Dec | 4 | 2019
Feb | 7 | 2020
Nov | 9 | 2019
From the above data; any data that is between now and past 6 months has a TIME instead of YEAR. However i want to write a query to attach the respective year instead of time.
I have below query which will attach the current year, however in case our data will lie in the months transitioning previous year it wont be accurate.
SELECT
DS_MONTH || '-' || DS_DAY || '-' ||
CASE
WHEN DS_YEAR LIKE '%:%' THEN TO_CHAR(sysdate, 'YYYY')
ELSE DS_YEAR
END dsf
FROM
MY_TABLE
How do i check the month whether it lies in current year or previous year, so that i can assign the correct year?
Example: if today was FEBRUARY. and in my data i have Sep | 1 | 06:00 then my query should return Sep-1-2019
and if today was OCTOBER it should return Sep-1-2020
Is this what you want ?
WITH sampledata (mon, d, yt) AS
(
SELECT 'Mar','2','2019' FROM DUAL UNION
SELECT 'Jan','4','2020' FROM DUAL UNION
SELECT 'Apr','2','07:43' FROM DUAL UNION
SELECT 'Sep','1','06:00' FROM DUAL UNION
SELECT 'Jul','2','05:00' FROM DUAL UNION
SELECT 'Dec','4','2019' FROM DUAL UNION
SELECT 'Feb','7','2020' FROM DUAL UNION
SELECT 'Nov','9','2019' FROM DUAL
),rundate (dt) AS
(
SELECT DATE'2021-05-30' FROM DUAL
)
SELECT s.mon, s.d, s.yt,
CASE WHEN TO_DATE(s.mon||'-'||s.d||'-'|| extract(year from r.dt),'Mon-dd-YYYY') > r.dt THEN TO_DATE(s.mon||'-'||s.d||'-'|| extract(year from ADD_MONTHS(r.dt,-12)),'Mon-dd-YYYY')
ELSE TO_DATE(s.mon||'-'||s.d||'-'|| extract(year from r.dt),'Mon-dd-YYYY')
END
FROM sampledata s CROSS JOIN rundate r
WHERE INSTR(s.yt,':') > 0
UNION
SELECT s.mon, s.d, s.yt, TO_DATE(s.mon||'-'||s.d||'-'|| s.yt,'Mon-dd-YYYY')
FROM sampledata s
WHERE INSTR(s.yt,':') = 0;
UPDATE: Added the 'rundate' CTE so you can test with any date, not just sysdate. Also added the case expression to check ensure the date is in the past.
This is just pseudo code, may require few changes in date/month conversion as per db syntax
SELECT
DS_MONTH || '-' || DS_DAY || '-' ||
CASE
WHEN DS_YEAR LIKE '%:%' and Month(sysdate)>6 THEN TO_CHAR(sysdate, 'YYYY')
WHEN DS_YEAR LIKE '%:%' and Month(cast(DS_MONTH+'1 2015' as datetime)) < 6 THEN TO_CHAR(sysdate, 'YYYY')
WHEN DS_YEAR LIKE '%:%' and Month(cast(DS_MONTH+'1 2015' as datetime)) > 6 THEN TO_CHAR(DATEADD(YEAR, -1, GETDATE()), 'YYYY')
ELSE DS_YEAR
END dsf
FROM
MY_TABLE

Oracle count days per month

I wrote this SQL statement to calculate the days for each month
(select count(*) DAYs FROM
(
select trunc(ADD_MONTHS(sysdate,-1),'MM') + level -1 Dates from dual connect by
level <= ADD_MONTHS(trunc(sysdate,'MM'),1)-1 - trunc(sysdate,'MM')+1
) Where To_char(dates,'DY') NOT IN ('SA','SO'))
At the moment this statement ignores Saturdays and Sundays and it calculates the days from the month before the sysdate (June).
June has 22 days without weekends but sadly my statement says it has 23. I found out it includes the 1st July, which is wrong.
Do you know how I can tell my little statement it only calculates the days from the month I want to get not including days from another month?
Doing this sort of thing is always going to look not pretty... here's one way, which does it for the entire current year. You can restrict to a single month by adding an additional statement to the where clause:
select to_char(trunc(sysdate, 'y') + level - 1, 'fmMON') as month, count(*)
from dual
where to_char(trunc(sysdate, 'y') + level - 1, 'fmDY', 'nls_date_language=english') not in ('SAT','SUN')
connect by level <= trunc(add_months(sysdate, 12), 'y') - trunc(sysdate, 'y')
group by to_char(trunc(sysdate, 'y') + level - 1, 'fmMON')
As I said, not pretty.
Note a couple of things:
Use of the fm format model modifier to remove leading spaces
Explicit use of nls_date_language to ensure it'll work in all environments
I've added 12 months to the current date and then truncated it to the first of January to get the first day of the new year for simplicity
If you want to do this by month it might be worth looking at the LAST_DAY() function
The same statement (using LAST_DAY()) for the previous month only would be:
select count(*)
from dual
where to_char(trunc(sysdate, 'y') + level - 1, 'fmDY', 'nls_date_language=english') not in ('SAT','SUN')
connect by level <= last_day(add_months(trunc(sysdate, 'mm'), -1)) - add_months(trunc(sysdate, 'mm'), -1) + 1
Firstly, your inner query (select trunc(ADD_MONTHS(sysdate,-1),'MM') + level -1 Dates from dual connect by level <= ADD_MONTHS(trunc(sysdate,'MM'),1)-1 - trunc(sysdate,'MM')+1) returns the days of the month plus one extra day from the next month.
Secondly, a simpler query could use the LAST_DAY function which gets the last day of the month.
Finally, use the 'D' date format to get the day of the week as a number.
SELECT COUNT(*) FROM (
SELECT TO_CHAR(TRUNC(SYSDATE,'MM') + ROWNUM - 1, 'D') d
FROM dual CONNECT BY LEVEL <= TO_NUMBER(TO_CHAR(LAST_DAY(SYSDATE),'DD'))
) WHERE d BETWEEN 1 AND 5;
Without having to generate all days of the month and then count them:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE FUNCTION WORK_DAYS_IN_MONTH(
dt DATE
) RETURN NUMBER DETERMINISTIC
AS
first_day DATE := TRUNC( dt, 'MM' );
remainder NUMBER := LAST_DAY( dt ) - ( first_day + INTERVAL '27' DAY );
BEGIN
RETURN 20 + CASE first_day - TRUNC( first_day, 'IW' )
WHEN 0 THEN remainder -- Monday
WHEN 1 THEN remainder -- Tuesday
WHEN 2 THEN remainder -- Wednesday
WHEN 3 THEN LEAST( remainder, 2 ) -- Thursday
WHEN 4 THEN LEAST( remainder, 1 ) -- Friday
WHEN 5 THEN GREATEST( remainder-2, 0 ) -- Saturday
ELSE GREATEST( remainder-1, 0 ) -- Sunday
END;
END;
//
Query 1:
SELECT ADD_MONTHS( DATE '2014-12-01', LEVEL ) AS "Month",
WORK_DAYS_IN_MONTH( ADD_MONTHS( DATE '2014-12-01', LEVEL ) ) AS "# Work Days"
FROM DUAL
CONNECT BY LEVEL <= 12
Results:
| Month | # Work Days |
|-----------------------------|-------------|
| January, 01 2015 00:00:00 | 22 |
| February, 01 2015 00:00:00 | 20 |
| March, 01 2015 00:00:00 | 22 |
| April, 01 2015 00:00:00 | 22 |
| May, 01 2015 00:00:00 | 21 |
| June, 01 2015 00:00:00 | 22 |
| July, 01 2015 00:00:00 | 23 |
| August, 01 2015 00:00:00 | 21 |
| September, 01 2015 00:00:00 | 22 |
| October, 01 2015 00:00:00 | 22 |
| November, 01 2015 00:00:00 | 21 |
| December, 01 2015 00:00:00 | 23 |

How to use generate_series to output the week startDate and endDate when input is MonYYYY in PostgreSQL

Currently I have that the following ORACLE code that outputs the startDate and EndDate of weeks of a given MonthYear as an input.
WITH t
AS (SELECT To_date('Jan2014', 'MonYYYY') orig_date,
Next_day(To_date('Jan2014', 'MonYYYY') - 7, 'MONDAY') start_day
FROM dual)
SELECT start_day + ( 7 * ( LEVEL - 1 ) ),
start_day + ( 7 * ( LEVEL - 1 ) ) + 6
FROM t,
dual
CONNECT BY start_day + ( 7 * ( LEVEL - 1 ) ) <
Add_months(Trunc(orig_date, 'MM'), 1);
The desired o/p should be when input is Jan2014 is
30 Dec 2013 to 05 Jan 2014,
06 Jan 2014 to 12 Jan 2014,
13 Jan 2014 to 19 Jan 2014,
20 Jan 2014 to 26 Jan 2014,
27 Jan 2014 to 02 Feb 2014
How can I use the PostgreSQL generate_series or similar postgreSQL code to output the desired startDate and EndDate in above format ??
SELECT to_char(d, 'DD Mon YYYY" to "')
|| to_char(d+6, 'DD Mon YYYY') AS week
FROM (
SELECT generate_series(d1
,d1 + interval '4 weeks'
,interval '1 week')::date AS d
FROM (SELECT date_trunc('week', to_date('Jan2014', 'MonYYYY')) AS d1) sub1
) sub2
Output as requested.
-> SQLfiddle
The following query generates the same output as Oracle:
select cast('2013-12-30' as date) + n*7 as startdate,
cast('2013-12-30' as date) + n*7+6 as enddate
from generate_series(0, 4) n;
EDIT:
Here is another method:
select prevMon + interval '1' day * n*7 as startdate,
prevMon + interval '1' day * (n*7+6) as enddate
from (select cast(const.yyyymm||'-01' as date) - interval '1' day * (extract(isodow from cast(const.yyyymm||'-01' as date))) as prevMon
from (select cast('2013-01' as varchar(255)) as yyyymm) const
) t cross join
generate_series(0, 4) n;
Note it changes the expression for year and month to YYYYMM.