Add subquery in query - Oracle SQL - sql

My query return all days in month.
SELECT
EXTRACT( DAY FROM day ) ||' '|| substr(TO_CHAR( day, 'fmDAY' ),0,3) AS day,
EXTRACT( DAY FROM day ) as day_id
FROM (
WITH temp ( col ) AS (
SELECT to_date(2, 'mm') --2 is February
FROM dual
)
SELECT col + level - 1 AS day
FROM temp
CONNECT BY level <= last_day(col) - col + 1
ORDER BY day
)
How get all days from query where DAY_ID not in ==> (Select day_id from table1)
Eg. table1 return 5,10,15
Query resault need to display all days except 5,10,15

You can generate your calendar and then use NOT EXISTS:
WITH month ( col ) AS (
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), 2 - 1)
FROM DUAL
),
calendar ( day, end_day ) AS (
SELECT col, LAST_DAY(col)
FROM month
UNION ALL
SELECT day + INTERVAL '1' DAY, end_day
FROM calendar
WHERE day < end_day
)
SELECT EXTRACT( DAY FROM day ) ||' '|| TO_CHAR( day, 'fmDY' ) AS day,
EXTRACT( DAY FROM day ) as day_id
FROM calendar c
WHERE NOT EXISTS (
SELECT 1
FROM table1 t
WHERE c.day = t.day_id
)
ORDER BY c.day;
Which, for your sample data:
CREATE TABLE table1 ( day_id ) AS
SELECT DATE '2021-02-05' FROM DUAL UNION ALL
SELECT DATE '2021-02-10' FROM DUAL UNION ALL
SELECT DATE '2021-02-15' FROM DUAL;
Outputs:
DAY
DAY_ID
1 MON
1
2 TUE
2
3 WED
3
4 THU
4
6 SAT
6
7 SUN
7
8 MON
8
9 TUE
9
11 THU
11
12 FRI
12
13 SAT
13
14 SUN
14
16 TUE
16
17 WED
17
18 THU
18
19 FRI
19
20 SAT
20
21 SUN
21
22 MON
22
23 TUE
23
24 WED
24
25 THU
25
26 FRI
26
27 SAT
27
28 SUN
28
db<>fiddle here

One way to generate all rows in month for dates in another table is to use a recursive CTE:
create table table1 as
select date '2021-04-16' as day_in from dual;
with cte (dte) as (
select trunc(day_in, 'MON') as dte
from table1
union all
select dte + interval '1' day
from cte
where dte < last_day(dte)
)
select *
from cte;
Here is a db<>fiddle.
EDIT:
If you want the days not in a table, then use:
with cte (dte) as (
select date '2021-02-01' as dte
from dual
union all
select dte + interval '1' day
from cte
where dte < last_day(dte)
)
select dte
from cte
where not exists (select 1 from table1 t1 where t1.day_id = cte.dte);
You can also use not exists using your query, but I find the recursive CTE easier to follow -- and it is standard SQL.

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')

Oracle query to get end of week from the given data set

I have table in Oracle , where EOW columns indicate the end of week.
I want to write a query to get the nearest end of week date.
Table Cal
DAY DAY OFTHE WEEK EOW
20181026 FRI Y
20181027 SAT N
20181028 SUN N
20181029 MON N
20181030 TUE N -->
20181031 WED N
20181101 THU N
20181102 FRI Y -->
20181103 SAT N
So when I
select DAY , "logic" from cal where day = 20181030;
What should be "logic" so that I get the nearest end of week date , in this case
20181026.
Please help!!
Do you really need a fixed table for that? A CTE can easily create any calendar you want, so - I took that freedom to produce something like this.
I wrote it step-by-step so that you could follow its execution. Start from the first CTE (dates), then go to day_diff, and so forth). It seems that you are selecting the first FRI that precedes current date. Because, for 20181030, the nearest end-of-week isn't 20181026 (4 days to that Friday) but 20181102 (3 days to that Friday).
At the end, the result is
SQL> with dates as
2 (select
3 -- add "level" (sequence of numbers from 1 to 60) to 1st of previous month
4 trunc(add_months(sysdate, - 1), 'mm') + level - 1 datum,
5 -- convert that date into a day name (MON, FRI, ...)
6 to_char(trunc(add_months(sysdate, -1), 'mm') + level - 1, 'DY',
7 'NLS_DATE_LANGUAGE=ENGLISH') dan,
8 -- if day name is FRI, set EOW = Y. Else, it is N
9 case when to_char(trunc(add_months(sysdate, -1), 'mm') + level - 1, 'DY',
10 'NLS_DATE_LANGUAGE=ENGLISH') = 'FRI' then 'Y'
11 else 'N'
12 end eow
13 from dual
14 connect by level <= 60 -- my CTE will have 60 dates; yours can have any number
15 ),
16 day_diff as
17 (select datum, dan, eow,
18 datum - to_date('&par_datum', 'dd.mm.yyyy') diff
19 from dates
20 ),
21 diff_only_eow as
22 (select datum, dan, eow, diff,
23 row_number() over (order by diff desc) rn
24 from day_diff
25 where eow = 'Y'
26 and diff <= 0
27 )
28 select datum, dan, eow, diff, rn
29 from diff_only_eow
30 where rn = 1;
Enter value for par_datum: 30.10.2018
DATUM DAN E DIFF RN
-------- ------------ - ---------- ----------
20181026 FRI Y -4 1
SQL>
What I am guessing is,
If the date is 30-OCT-2018 (20181030), then you want last friday date as 26-OCT - 2018 (20181026).
Another Scenario :
If the date is 27-OCT-2018 (20181027), in that case also you want last friday date which is 26-OCT - 2018 (20181026).
If my this is guess is true then below query may work :
WITH TEMP1 AS
(
SELECT TO_CHAR(
TO_DATE('20181030','YYYYMMDD') ,'DD-MON-YY') AS DATE_TEST
FROM DUAL
)
SELECT next_day (TO_DATE(DATE_TEST,'DD-MON-YY')-7,'FRIDAY') Last_Friday
FROM TEMP1;
It will display output as :
LAST_FRIDAY
26-OCT-18
Which you can convert later in your required format.
Test Case 2
WITH TEMP1 AS
(
SELECT TO_CHAR(
TO_DATE('20181103','YYYYMMDD') ,'DD-MON-YY') AS DATE_TEST
FROM DUAL
)
SELECT next_day (TO_DATE(DATE_TEST,'DD-MON-YY')-7,'FRIDAY') Last_Friday
FROM TEMP1;
Output :
LAST_FRIDAY
02-NOV-18
Now breaking of above query :
WITH clause is used in CTE.
WITH TEMP1 AS
(
SELECT TO_CHAR(
TO_DATE('20181103','YYYYMMDD') ,'DD-MON-YY') AS DATE_TEST
FROM DUAL
)
Here, TO_DATE('20181103','YYYYMMDD') ,'DD-MON-YY' - will convert 20181103 as 03-NOV-2018.
So the result from the WITH clause (Which is 03-NOV-2018) will be used in another query :
SELECT next_day (TO_DATE(DATE_TEST,'DD-MON-YY')-7,'FRIDAY') Last_Friday
FROM TEMP1;
Here DATE_TEST is output from with clause. First
TO_DATE(DATE_TEST,'DD-MON-YY')-7
It is taking previous 7 days from the mentioned date (which is currently DATE_TEST : 03-NOV -2018) So It will take all the last 7 days from 03-NOV-2018.
Assuming :
DATE DAY Order
28-10-2018 SUN 1
29-10-2018 MON 2
30-10-2018 TUE 3
31-10-2018 WED 4
01-11-2018 THURS 5
02-11-2018 FRI 6
03-11-2018 SAT 7
We got all 7 days mentioned above.
next_day (TO_DATE(DATE_TEST,'DD-MON-YY')-7,'FRIDAY')
Now from next_day, we can get another day and here, we are asking for FRIDAY by mentioning it in the argument. So FRIDAY is 02-11-2018.
So the output will be 02-11-2018.
This may solve your query
select day, to_char(to_date(day,'YYYYMMDD'),'DY') f1,
case to_char(to_date(day,'YYYYMMDD'),'DY')
when 'FRI' then 0
when 'SAT' then -1
when 'SUN' then -2
when 'MON' then -3
when 'TUE' then 3
when 'WED' then 2
when 'THU' then 1
end as f2,
to_char(to_date(day,'YYYYMMDD')+ case to_char(to_date(day,'YYYYMMDD'),'DY')
when 'FRI' then 0
when 'SAT' then -1
when 'SUN' then -2
when 'MON' then -3
when 'TUE' then 3
when 'WED' then 2
when 'THU' then 1
end,'YYYYMMDD') as f3
from test_cal;
DAY F1 F2 F3
-------- --------- ---------- --------
20181026 FRI 0 20181026
20181027 SAT -1 20181026
20181028 SUN -2 20181026
20181029 MON -3 20181026
20181030 TUE 3 20181102
20181031 WED 2 20181102
20181101 THU 1 20181102
20181102 FRI 0 20181102
20181103 SAT -1 20181102
9 rows selected.

sql oracle ignore holidays

I am using this code to calculate the difference between two dates ignoring weekends:
SELECT To_date(SYSDATE) -
To_date('01.07.2014', 'DD.MM.YYYY')
- 2 * ( TRUNC(Next_day(To_date(SYSDATE) - 1, 'FRI'))
- TRUNC( Next_day(To_date('01.07.2014' , 'DD.MM.YYYY')
- 1, 'FRI')) ) / 7 AS DAYS_BETWEEN
FROM dual
I have another table called table1 in which the column "date" exists (its type is "DATE") in which all dates where a holiday is are written down.
Example table 1:
DATES
12.06.2011
19.06.2014
09.05.2013
...
I am trying to make my code check this table and that if one date is between the two dates above it makes -1 day in the output.
It should be easy if you divide it into following tasks:
Generate all the dates between the two given dates using Row Generator method as shown here.
Ignore the dates which are weekend, i.e. Saturdays and Sundays
Check whether the dates in the range are having any match in the holiday table.
The following row generator query will give you the total count of weekdays, i.e. not including Saturdays and Sundays:
SQL> WITH dates AS
2 (SELECT to_date('01/01/2014', 'DD/MM/YYYY') date1,
3 to_date('31/12/2014', 'DD/MM/YYYY') date2
4 FROM dual
5 )
6 SELECT SUM(weekday) weekday_count
7 FROM
8 (SELECT
9 CASE
10 WHEN TO_CHAR(date1+LEVEL-1, 'DY','NLS_DATE_LANGUAGE=AMERICAN')
11 NOT IN ('SAT', 'SUN')
12 THEN 1
13 ELSE 0
14 END weekday
15 FROM dates
16 CONNECT BY LEVEL <= date2-date1+1
17 )
18 /
WEEKDAY_COUNT
-------------
261
SQL>
Now, based on above row generator query, let's see a test case.
The following query will calculate the count of working days between 1st Jan 2014 and 31st Dec 2014 excluding the holidays as mentioned in the table.
The WITH clause is only to use it as tables, in your case you can simply use your holiday table.
SQL> WITH dates
2 AS (SELECT To_date('01/01/2014', 'DD/MM/YYYY') date1,
3 To_date('31/12/2014', 'DD/MM/YYYY') date2
4 FROM dual),
5 holidays
6 AS (SELECT To_date('12.06.2011', 'DD.MM.YYYY') holiday FROM dual UNION ALL
7 SELECT To_date('19.06.2014', 'DD.MM.YYYY') holiday FROM dual UNION ALL
8 SELECT To_date('09.05.2013', 'DD.MM.YYYY') holiday FROM dual),
9 count_of_weekdays
10 AS (SELECT SUM(weekday) weekday_count
11 FROM (SELECT CASE
12 WHEN To_char(date1 + LEVEL - 1, 'DY',
13 'NLS_DATE_LANGUAGE=AMERICAN')
14 NOT IN (
15 'SAT',
16 'SUN' ) THEN 1
17 ELSE 0
18 END weekday
19 FROM dates
20 CONNECT BY LEVEL <= date2 - date1 + 1)),
21 count_of_holidays
22 AS (SELECT Count(*) holiday_count
23 FROM holidays
24 WHERE holiday NOT BETWEEN To_date('01/01/2015', 'DD/MM/YYYY') AND
25 To_date('31/03/2015', 'DD/MM/YYYY'))
26 SELECT weekday_count - holiday_count as working_day_count
27 FROM count_of_weekdays,
28 count_of_holidays
29 /
WORKING_DAY_COUNT
-----------------
258
SQL>
There were total 261 weekdays, out of which there were 3 holidays in holiday table. So, total count of working days in the output is 261 - 3 = 258.
SELECT To_date(sysdate)- To_date('01.07.2014','DD.MM.YYYY')
- (2 * (to_char(To_date(sysdate), 'WW') - to_char(To_date('01.07.2014','DD.MM.YYYY'), 'WW'))) AS DAYS_BETWEEN
FROM dual

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.

SSRS PIVOT TABLE QUERY to return days absent by month

I have an SQL query with
startdate 23/09/2013
enddate 06/10/2013
days_absent 14
and I need to show the days _absent in a pivot table as Sep 7 and Oct 7
instead of Sep 14
Month Days_Absent
------------------------
SEP 7
OCT 7
Here is the query to populate the report:
SELECT TO_CHAR(ACCINJ_VIEW.STARTDATEOFABSENCE,'DD/MM/YYYY') AS "Start Date of Absence" ,
TO_CHAR(ACCINJ_VIEW.ENDDATEOFABSENCE,'DD/MM/YYYY') AS "End Date of Absence" , ACCINJ_VIEW.DAYSABSENT AS "Days Absent" ,
FROM ACCINJ_VIEW ACCINJ_VIEW,
WHERE ACCINJ_VIEW.IDPERSONUNIQUEKEY = INJG_VIEW.IPERSONUNIQUEKEY(+) AND ov_VIEW.ACCDATE BETWEEN :ACCDATESTART AND :ACCDATEEND
I think this might work
WITH all_days AS
(
SELECT first_day + LEVEL - 1 AS a_day
FROM (
SELECT TO_DATE ( '05/08/2011' , 'DD/MM/YYYY') AS first_day, -- Start Date
TO_DATE ( '16/10/2011' , 'DD/MM/YYYY') AS last_day -- End Date
FROM dual
)
CONNECT BY LEVEL <= last_day + 1 - first_day
)
SELECT TO_CHAR (TRUNC(a_day,'MONTH'),'FMMonth YYYY') AS month,
COUNT (*) AS day
FROM all_days
GROUP BY TRUNC (a_day, 'MONTH')
ORDER BY TRUNC (a_day, 'MONTH')
/
Result:
MONTH DAY
-------------- ----------
August 2011 27
September 2011 30
October 2011 16