SELECT * FROM dummy;
act_date
---------
27-JAN-22
SELECT * FROM dummy1;
rpt_date
---------
10-JAN-22
10-DEC-21
how to get only Saturdays between act_date of dummy and MIN(rpt_date) of dummy1 table in Oracle's SQL?
please help
Here is a simple example of how to list all saturdays between 2 dates, you should be able to convert this to your data model.
WITH dummy(start_date, end_date) AS
(
SELECT DATE'2014-01-30', DATE'2014-02-25' FROM dual
), dummy_all_dates(dt) AS
(
SELECT
e.dt
FROM
dummy d CROSS APPLY
( SELECT
d.start_date + level - 1 AS dt
FROM dual CONNECT BY level < d.end_date - d.start_date
) e
)
SELECT dt FROM dummy_all_dates
WHERE TO_CHAR(dt,'FMDAY','NLS_DATE_LANGUAGE=english') = 'SATURDAY';
01-FEB-2014
08-FEB-2014
15-FEB-2014
22-FEB-2014
Here's another method to get a years worth of Saturdays. Adjust the dates as needed.
The to_char function has various format masks you can use to extract the day-of-week from a date. This uses day to get the full day name:
with rws as (
select date'2021-12-31' + level dt
from dual
connect by level <= (
date'2022-01-01' - date'2021-01-01'
)
)
select dt
from rws
where to_char (
dt,
'fmday',
'nls_date_language = English'
) = 'saturday';
This is the simplest of all answers:
select dt,rtrim(to_char(dt, 'DAY')) from (select to_date(SELECT
min(rpt_date)
FROM dummy1, 'DD-MON-YY') + rownum -1 dt
from all_objects
where rownum <= to_date((SELECT act_date FROM dummy), 'DD-MON-YY') -
to_date(SELECT min(rpt_date) FROM dummy1, 'DD-MON-YY'));
where rtrim(to_char(dt, 'DAY')) = 'SATURDAY';
Thanks!!
Related
I have read only access in my database and I have a table that looks like this;
I want an output that looks like the following
Is this possible? I think I would need to have a table of dates to pass through but I'm not sure how to go about that. Any help would be appreciated.
Use a sub-query factoring clause (WITH) to generate the dates to join:
WITH dates ( dt ) AS (
SELECT DATE '2016-07-01' FROM DUAL UNION ALL
SELECT DATE '2017-07-01' FROM DUAL UNION ALL
SELECT DATE '2019-08-01' FROM DUAL
)
SELECT EXTRACT(MONTH FROM d.dt) AS Month,
EXTRACT(YEAR FROM d.dt) AS Year,
NVL2(t.SomeValue, 'Y', 'N') AS "Change?"
FROM dates d
LEFT OUTER JOIN table_name t
ON ( TRUNC(t.effective_date, 'MM') = d.dt )
My desired output would include every month and year from 1/2012 on.
Then use a recursive sub-query factoring clause:
WITH dates ( dt ) AS (
SELECT DATE '2012-01-01' FROM DUAL
UNION ALL
SELECT ADD_MONTHS( dt, 1 )
FROM dates
WHERE ADD_MONTHS( dt, 1 ) <= SYSDATE
)
SELECT EXTRACT(MONTH FROM d.dt) AS Month,
EXTRACT(YEAR FROM d.dt) AS Year,
NVL2(t.SomeValue, 'Y', 'N') AS "Change?"
FROM dates d
LEFT OUTER JOIN table_name t
ON ( TRUNC(t.effective_date, 'MM') = d.dt )
You can use recursion to break the records in to who years...
WITH
fragmented (
window_start,
window_close,
some_value,
interval_start,
interval_close
)
AS
(
SELECT
window_start,
window_close,
some_value,
interval_start,
CASE
WHEN add_months(interval_start, 12) < window_close
THEN add_months(interval_start, 12)
ELSE window_close
END
AS interval_close
FROM
(
SELECT
effective_date AS window_start,
LEAD(effective_date) OVER (ORDER BY effective_date) AS window_close,
some_value,
effective_date AS interval_start
FROM
example
)
lookahead
UNION ALL
SELECT
window_start,
window_close,
some_value,
add_months(interval_start, 12),
CASE
WHEN add_months(interval_start, 24) < window_close
THEN add_months(interval_start, 24)
ELSE window_close
END
FROM
fragmented
WHERE
interval_close < window_close
)
SELECT
*
FROM
fragmented
ORDER BY
window_start,
interval_start
;
Demo : https://dbfiddle.uk/?rdbms=oracle_18&fiddle=ca1fef00069c178c28e09d209db35395
With the code below I can return this month and past 6 months.
My code:
SELECT TO_CHAR(add_months(TRUNC(to_date( sysdate),'Month'), -rownum+1), 'Month') mon,
rownum month_order
FROM dual
CONNECT BY rownum <=
(SELECT COUNT(mon)
FROM
(SELECT TO_CHAR( add_months( start_date, level-1 ), 'fmMonth' ) AS mon
FROM
(SELECT to_date( add_months(TRUNC(sysdate),-6)) start_date,
to_date( sysdate) end_date
FROM dual)
CONNECT BY level <= months_between( TRUNC(end_date,'MM'), TRUNC(start_date,'MM') ) + 1) dual);
In the same way I have to return this date and past 9 dates.
Please help me on this to return 10 dates by using connect by.
Thanks in advance.
Here's a query for the last ten days.
select sysdate - (level-1)
, level as day_order
from dual
connect by level <= 10;
Your month query seems extremely over-engineered. This would do the same thing:
select to_char(add_months(trunc(sysdate, 'MM'), 1 - level), 'Month')
, level as month_order
from dual
connect by level <= 7;
"Let suppose if the data is available on today's date and remaining 9 date's doesn't have any data but it has to display the count as zero"
Use the generated result set in an outer join:
with q as (
select sysdate - (level-1) as dt
, level as day_order
from dual
connect by level <= 10
)
select q.dt as txn_date
, sum(t42.col1)
from q
left outer join t42
on t42.transaction_date = q.dt
group by q.dt;
Try this -
SELECT ( TRUNC( SYSDATE ) + 1 ) - ROWNUM
FROM DUAL
CONNECT BY ROWNUM <= 10
In the same way i have to return this date and past 9 dates.
This will get today and the past 9 days:
SELECT TRUNC( SYSDATE ) - LEVEL + 1 AS day
FROM DUAL
CONNECT BY LEVEL <= 10
this month and past 6 months
SELECT TO_CHAR( ADD_MONTHS( TRUNC( SYSDATE, 'MM' ), 1 - LEVEL ), 'Month' ) AS month
FROM DUAL
CONNECT BY LEVEL <= 7
Here are the dates available in my Record_Date (Date) column in Attendance table in Oracle 10g.You can find the dates 04/06/2016 08/06/2016 16/06/2016 23/06/2016 29/06/2016 are missing in the sequence.
**Record_Date**
01/06/2016
02/06/2016
03/06/2016
05/06/2016
06/06/2016
07/06/2016
09/06/2016
10/06/2016
12/06/2016
13/06/2016
14/06/2016
15/06/2016
17/06/2016
18/06/2016
19/06/2016
20/06/2016
21/06/2016
22/06/2016
24/06/2016
25/06/2016
26/06/2016
27/06/2016
28/06/2016
30/06/2016
01/07/2016
I just need a query to find the missing dates in the specific month (and later also in the Year).
Kindly show me an approach
You can use this one:
WITH all_days AS
(SELECT DATE '2016-06-01' + LEVEL-1 AS the_day
FROM dual
CONNECT BY DATE '2016-06-01' + LEVEL-1 <= DATE '2016-06-30')
SELECT the_day
FROM all_days
WHERE the_day <>ALL (SELECT Record_Date FROM Attendance);
Or, if you like to have it more dynamically:
WITH all_days AS
(SELECT START_DATE + LEVEL AS the_day
FROM dual
CROSS JOIN
(SELECT
TRUNC(MIN(Record_Date), 'MM') -1 AS START_DATE,
TRUNC(LAST_DAY(MAX(Record_Date))) AS END_DATE
FROM Attendance)
CONNECT BY START_DATE + LEVEL <= END_DATE)
SELECT the_day
FROM all_days
WHERE the_day <>ALL (SELECT Record_Date FROM Attendance);
Note, <>ALL is the same as NOT IN - it's just my personal preference.
with
nums(num )
as
(select 0 from dual
union all
select num + 1 from nums
where num < (select max(col) from qtable)- (select min(col) from qtable)
),
date_btwn(dat)
as(select num + (select min(col) from qtable) from nums)
select dat from date_btwn
minus
select col from qtable;
Inline views nums will generate all numbers to add from start date. date_btwn contains all dates between start date and end date in table. We are excluding the dates in our table using minus.
You need to generate all dates and you have to find missing ones. Below with cte i have done it
Using CTE and not inQuery :
with calendar as (
select rownum - 1 as daynum,
to_date('1-jun-2016') + (rownum - 1) as monthdate
from dual )
select monthdate as monthdate
from calendar
where monthdate not in (
SELECT
Record_Date
FROM Attendance )
and monthdate >= to_date('1-jun-2016') /* CHANGE as per need */
and monthdate < to_date('1-jul-2016') /* CHANGE as per need */
Or Using CTE and left join Query :
with calendar as (
select rownum - 1 as daynum,
to_date('1-jun-2016') + (rownum - 1) as monthdate
from dual )
select monthdate as monthdate
from calendar C left join Attendance A on A.Record_Date=c.monthdate
where A.Record_Date is null
and monthdate >= to_date('1-jun-2016') /* CHANGE as per need */
and monthdate < to_date('1-jul-2016') /* CHANGE as per need */
as per datecolumn format change it to validformat into selection query first then use this query.
I have a SQL query which displays count, date, and time.
This is what the output looks like:
And this is my SQL query:
select
count(*),
to_char(timestamp, 'MM/DD/YYYY'),
to_char(timestamp, 'HH24')
from
MY_TABLE
where
timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
group by
to_char(timestamp, 'MM/DD/YYYY'), to_char(timestamp, 'HH24')
Now, in COUNT column, I want to display 0 if the count doesn't exist for that hour. So on 3/2/2016 at 8am, the count was 6. Then at 9am the count was 0 so that row didn't get displayed. I want to display that row. And at 10am & 11am, the counts are displayed then it just goes to next day.
So how do I display count of 0? I want to display 0 count for each day every hour doesn't matter if it's 0 or 6 or whatever. Thanks :)
Use a partition outer join:
SELECT m.day,
h.hr,
COALESCE( freq, 0 ) AS freq
FROM ( SELECT LEVEL - 1 AS hr
FROM DUAL
CONNECT BY LEVEL <= 24
) h
LEFT OUTER JOIN
( SELECT COUNT(*) AS freq,
TO_CHAR( "timestamp", 'mm/dd/yyyy' ) AS day,
EXTRACT( HOUR FROM "timestamp" ) AS hr
FROM MY_TABLE
WHERE "timestamp" >= TIMESTAMP '2016-03-01 00:00:00'
GROUP BY
TO_CHAR( "timestamp", 'mm/dd/yyyy' ),
EXTRACT( HOUR FROM "timestamp" )
) m
PARTITION BY ( m.day, m.hr )
ON ( m.hr = h.hr );
Use a cte to generate numbers for all the hours in a day. Then cross join the result with all the possible dates from the table. Then left join on the cte which has all date and hour combinations, to get a 0 count when a row is absent for a particular hour.
with nums(n) as (select 1 from dual
union all
select n+1 from nums where n < 24)
,dateshrscomb as (select n,dt
from nums
cross join (select distinct trunc(timestamp) dt from my_table
where timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
) alldates
)
select count(trunc(m.timestamp)), d.dt, d.n
from dateshrscomb d
left join MY_TABLE m on to_char(m.timestamp, 'HH24') = d.n
and trunc(m.timestamp) = d.dt
and m.timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
group by d.dt, d.n
with cteHours(h) as (select 0 from dual
union all
select h+1 from cteHours where h < 24)
, cteDates(d) AS (
SELECT
trunc(MIN(timestamp)) as d
FROM
My_Table
WHERE
timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
UNION ALL
SELECT
d + 1 as d
FROM
cteDates
WHERE
d + 1 <= (SELECT trunc(MAX(timestamp)) FROM MY_TABLE)
)
, datesNumsCross (d,h) AS (
SELECT
d, h
FROM
cteDates
CROSS JOIN cteHours
)
select count(*), to_char(d.d, 'MM/DD/YYYY'), d.h
from datesNumsCross d
LEFT JOIN MY_TABLE m
ON d.d = trunc(m.timestamp)
AND d.h = to_char(m.timestamp, 'HH24')
group by d.d, d.h
#VPK is doing a good job at answering, I just happened to be writing this at the same time as his last edit to generate a date hour cross join. This solution differs from his in that it will get all dates between your desired max and min. Where as his will get only the dates within the table so if you have a day missing completely it would not be represented in his but would in this one. Plus I did a little clean up on the joins.
Here is one way to do that.
Using Oracle's hierarchical query feature and level psuedo column, generate the dates and hours.
Then do an outer join of above with your data.
Need to adjust the value of level depending upon your desired range (This example uses 120). Start date needs to be set as well. It is ( trunc(sysdate, 'hh24')-2/24 ) in this example.
select nvl(c1.cnt, 0), d1.date_part, d1.hour_part
from
(
select
to_char(s.dt - (c.lev)/24, 'mm/dd/yyyy') date_part,
to_char(s.dt - (c.lev)/24, 'hh24') hour_part
from
(select level lev from dual connect by level <= 120) c,
(select trunc(sysdate, 'hh24')-2/24 dt from dual) s
where (s.dt - (c.lev)/24) < trunc(sysdate, 'hh24')-2/24
) d1
full outer join
(
select
count(*) cnt,
to_char(timestamp, 'MM/DD/YYYY') date_part,
to_char(timestamp, 'HH24') hour_part
from
MY_TABLE
where
timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
group by
to_char(timestamp, 'MM/DD/YYYY'), to_char(timestamp, 'HH24')
) c1
on d1.date_part = c1.date_part
and d1.hour_part = c1.hour_part
What is the easiest way to find difference in days between 2 dates separately for each month.
Let's suppose I have two dates
1.25 May 2016
2.08 Jun 2016
The result I want to obtain is number of days for May and numbers of Day for Jun like this (201605-6,201606-8).
Which is the best method to do this?
In your case, start_date and end_date are in the same row of a table so one solution is:
Idea: create a function with 2 input: start_date and end_date, output: string_with_days_in_each_month_between_two_date
Practice: First, create a function GET_DAYS_EACH_MONTH_BETWEEN return the expected string with each start_date and end_date
CREATE OR REPLACE FUNCTION GET_DAYS_EACH_MONTH_BETWEEN
(start_date IN date, end_date IN date)
RETURN varchar2 IS
result varchar2(4000);
BEGIN
WITH tmp AS
(SELECT
LAST_DAY(ADD_MONTHS( start_date, level-1 ))
FROM
(SELECT
start_date
,end_date
FROM
dual)
CONNECT BY
level <= MONTHS_BETWEEN(
TRUNC(end_date,'MM'),
TRUNC(start_date,'MM') )
)
, tmp1 AS
(
SELECT start_date date_col FROM dual
UNION ALL
SELECT end_date from dual
UNION ALL
SELECT * FROM tmp
)
SELECT
LISTAGG(result_col, ',') WITHIN GROUP (ORDER BY date_col) into result
FROM
(SELECT
TO_CHAR(date_col,'yyyyMM') || '_' || TO_CHAR(date_col - LAG(date_col) OVER (ORDER BY date_col)) result_col
, date_col
,ROW_NUMBER() OVER (ORDER BY date_col) rwn
FROM
tmp1
ORDER BY
date_col
)
WHERE
rwn <> 1;
RETURN result;
END;
Second, apply your function to your table
WITH test_table AS
(
SELECT
TO_DATE('2016-02-06','yyyy-mm-dd') start_date
,TO_DATE('2016-06-08','yyyy-mm-dd') end_date
FROM
DUAL
UNION ALL
SELECT
TO_DATE('2016-05-06','yyyy-mm-dd')
,TO_DATE('2016-06-08','yyyy-mm-dd')
FROM
DUAL
)
SELECT
start_date, end_date
,GET_DAYS_EACH_MONTH_BETWEEN(start_date, end_date) result_col
FROM
test_table;
Result:
start_date end_date result_col
2/6/2016 6/8/2016 201602_23,201603_31,201604_30,201605_31,201606_8
5/6/2016 6/8/2016 201605_25,201606_8
IMPORTANCE NOTICE: the result string only have 4000 char in length, so you should consider when start_date and end_date is too far from each
The function using suggested result from this link to gerenate all months between 2 given date.
I made a recursive approach:
with recur (dias, texto, nivel, d1,d2) as
(select last_day(d1)-d1 as dias, to_char(d1,'yyyymm')||'-' as texto, 0 as nivel, d1,d2 from dates union all --first date
select case when (d2-trunc(add_months(d1, nivel+1),'month')+1) > extract(day from last_day(add_months(d1, nivel+1)))
then extract(day from last_day(add_months(d1, nivel+1)))
else d2-trunc(add_months(d1, nivel+1),'month')+1 end as dias,
to_char(add_months(d1, nivel+1),'yyyymm')||'-' as texto, nivel+1 as nivel ,d1,d2 from recur where trunc(add_months(d1, nivel+1),'month') < d2 --last month
)
select d1, d2,listagg(texto||to_char(dias),',') within group (order by nivel) as resultado
from recur
group by d1,d2
order by d1,d2;
OUTPUT for 25 May 2016 to 08 Jun 2016
25-MAY-16 08-JUN-16 201605-6,201606-8
OUTPUT for 25 May 2016 to 08 Jul 2017
25-MAY-16 08-JUL-17 201605-6,201606-30,201607-31,201608-31,201609-30,201610-31,201611-30,201612-31,201701-31,201702-28,201703-31,201704-30,201705-31,201706-30,201707-8