I want to connect the following two queries with union all.
First query is following:
WITH
week_source
AS
(SELECT week_desc,
TO_CHAR (day_date, 'IYYY') || 'W' || TO_CHAR (day_date, 'IW')
week,
(SELECT DISTINCT day_number
FROM period_day
WHERE day_key = d.day_key AND day_number NOT IN ('H', 'W') AND WEEK_NUM !=to_char(sysdate, 'WW')
)
workdays_count
FROM period_day d
WHERE TO_CHAR (day_date, 'IYYYIW') BETWEEN TO_CHAR (
(SYSDATE - 1) - 28,
'IYYYIW')
AND TO_CHAR (
(SYSDATE - 1),
'IYYYIW')
)
SELECT DISTINCT week_desc, week, workdays_count
FROM week_source
WHERE workdays_count IS NOT NULL
ORDER BY week;
It gives following table
The second query is:
SELECT 'W' || TO_CHAR (SYSDATE - 1, 'IW') WEEK_DESC,
TO_CHAR (SYSDATE - 1, 'IYYYIW') WEEK,
COUNT (day_date) WORKDAYS_COUNT
FROM period_day
WHERE day_number NOT IN ('H', 'W')
AND TO_CHAR (day_date, 'IYYYIW') =
TO_CHAR ((SYSDATE - 1), 'IYYYIW')
AND day_date <= (SYSDATE - 1)
which gives the following table
Any help?
You have to put order by clauseat the end.
If the first query returns 3 columns with data types varchar, int, varchar then the second query has to return columns of same data types.
WITH week_source AS
(SELECT week_desc,
TO_CHAR (day_date, 'IYYY') || 'W' || TO_CHAR (day_date, 'IW') week,
(SELECT DISTINCT day_number
FROM period_day
WHERE day_key = d.day_key
AND day_number NOT IN ('H', 'W')
AND WEEK_NUM !=to_char(sysdate, 'WW')) workdays_count
FROM period_day d
WHERE TO_CHAR (day_date, 'IYYYIW') BETWEEN
TO_CHAR ((SYSDATE - 1) - 28, 'IYYYIW')
AND
TO_CHAR ((SYSDATE - 1),'IYYYIW'))
SELECT DISTINCT week_desc, week, workdays_count
FROM week_source
WHERE workdays_count IS NOT NULL
union all
SELECT 'W' || TO_CHAR (SYSDATE - 1, 'IW') WEEK_DESC,
TO_CHAR (SYSDATE - 1, 'IYYYIW') WEEK,
to_char(COUNT(day_date)) WORKDAYS_COUNT
FROM period_day
WHERE day_number NOT IN ('H', 'W')
AND TO_CHAR (day_date, 'IYYYIW') = TO_CHAR ((SYSDATE - 1), 'IYYYIW')
AND day_date <= (SYSDATE - 1)
ORDER BY week
In this DEMO you can see the errors returning when the order by is located after the first query. Also you can see what error you get if I just remove the order by clause and this second error is because data types that first query is returning is different that the data types second row is returning. That is why I have added to_char to your count function in your second query. You could of also add to_number in your first query like this: SELECT DISTINCT to_number(day_number).
Related
I need help with my query
select distinct count(item_number), creation_date
from EGP_SYSTEM_ITEMS_B ,
all I need to count the item number every month
for example
3-9-2020 count:29700
4-9-2020 count:29600
5-9-2020 Count:30000
and get the all date for the month and the previous month from creation_id or sysdate any of them
thanks
To count the number per month, you would aggregate by the month:
select trunc(creation_date, 'MON') as yyyymm, count(*)
from EGP_SYSTEM_ITEMS_B
group by trunc(creation_date, 'MON');
Your question is not entirely clear; perhaps you're trying to get
select TRUNC(CREATION_DATE), count(item_number)
from EGP_SYSTEM_ITEMS_B
WHERE TRUNC(CREATION_DATE)
IN (ADD_MONTHS(TRUNC(SYSDATE), -1),
TRUNC(SYSDATE),
ADD_MONTHS(TRUNC(SYSDATE), 1))
GROUP BY TRUNC(CREATION_DATE)
EDIT
Apparently OP wants a running monthly count, so something like:
WITH cteLimits (START_DATE, END_DATE)
AS (SELECT ADD_MONTHS(TRUNC(SYSDATE), -2), ADD_MONTHS(TRUNC(SYSDATE), -1) - INTERVAL '1' DAY FROM DUAL UNION ALL
SELECT ADD_MONTHS(TRUNC(SYSDATE), -1), TRUNC(SYSDATE) - INTERVAL '1' DAY FROM DUAL UNION ALL
SELECT TRUNC(SYSDATE), ADD_MONTHS(TRUNC(SYSDATE), 1) - INTERVAL '1' DAY FROM DUAL),
cteDay_totals
AS (SELECT TRUNC(CREATION_DATE) AS CREATION_DATE,
COUNT(*) AS DAY_TOTAL
FROM EGP_SYSTEM_ITEMS_B
GROUP BY TRUNC(CREATION_DATE))
SELECT l.START_DATE,
l.END_DATE,
SUM(d.DAY_TOTAL) AS MONTH_TOTAL
FROM cteLimits l
INNER JOIN cteDay_totals d
ON d.CREATION_DATE BETWEEN l.START_DATE AND l.END_DATE
GROUP BY l.START_DATE,
l.END_DATE
I have a report which should display enrollment data only within 2 date ranges Jan-June or July-dec depending on current date.
Scenarios:
If the current date is 042020 then I should display enrollement data between this range: 072019-122019
If the current date is 072020 then I should display enrollement data between this range: 012020-062020
If the current date is 022021 then I should display enrollement data between this range: 072020-122020
Current query reports everything past 6 months with his query.
select * from enrollement where enrollement_dt > add_months(sysdate - 6);
Is there any function available in oracle to do the same or how do i get the logic in a single statement?
Any help with this is highly appreciated.
You may try below query -
select *
from enrollement
WHERE TO_CHAR(enrollement_dt, 'MMYYYY') >= CASE WHEN TO_CHAR(SYSDATE, 'mm') <= '06'
THEN TO_DATE('07' || EXTRACT(YEAR FROM SYSDATE) - 1, 'MMYYYY')
ELSE THEN TO_DATE('01' || EXTRACT(YEAR FROM SYSDATE), 'MMYYYY')
END
AND TO_CHAR(enrollement_dt, 'MMYYYY') <= CASE WHEN TO_CHAR(SYSDATE, 'mm') <= '06'
THEN TO_DATE('12' || EXTRACT(YEAR FROM SYSDATE) - 1, 'MMYYYY')
ELSE THEN TO_DATE('06' || EXTRACT(YEAR FROM SYSDATE), 'MMYYYY')
END
Basically you want to truncate to the half-year. But Oracle doesn't support this.
One method counts half-years and compares them. You want the previous half year from the current date. That would be:
select (extract(year from sysdate) * 2 + floor(extract(month from sysdate) - 1) / 6) - 1
from dual
You can use this same formula:
where (extract(year from enrollement_dt) * 2 + floor(extract(month from enrollement_dt) - 1) / 6) - 1 =
extract(year from sysdate) * 2 + floor(extract(month from sysdate) - 1) / 6) - 1
)
from dual;
Unfortunately that can't use an index on the column. So, we can revisit this. You can get the first day of the current half using some date arithmetic:
select trunc(sysdate, 'Q') - mod(floor((extract(month from sysdate) - 1) / 3), 2) * interval '3' month
from dual
That just needs to be plugged into a where clause:
where enrollement_dt >= trunc(sysdate, 'Q') - mod(floor((extract(month from sysdate) - 1) / 3), 2) * interval '3' month - interval '6' month and
enrollement_dt < trunc(sysdate, 'Q') - mod(floor((extract(month from sysdate) - 1) / 3), 2) * interval '3' month
Voila! An expression that can even use an index.
You can use the below to get the start date and end date for enrollment
WITH data
AS (SELECT TRUNC(SYSDATE) curr_date from dual
),
d2
AS (SELECT curr_date,
To_date('0107'
||( Extract (year FROM curr_date) - 1 ), 'ddmmyyyy')
start_first_half,
To_date('3112'
||( Extract (year FROM curr_date) - 1 ), 'ddmmyyyy')
end_first_half,
To_date('0101'
||Extract (year FROM curr_date), 'ddmmyyyy')
start_second_half,
To_date('3006'
||Extract (year FROM curr_date), 'ddmmyyyy')
end_second_half
FROM data)
SELECT curr_date,
CASE
WHEN To_char(curr_date, 'MM') >= To_char(start_first_half, 'MM')
AND To_char(curr_date, 'MM') <= To_char(end_first_half, 'MM') THEN
start_second_half
ELSE start_first_half
END start_date1,
CASE
WHEN To_char(curr_date, 'MM') >= To_char(start_first_half, 'MM')
AND To_char(curr_date, 'MM') <= To_char(end_first_half, 'MM') THEN
end_second_half
ELSE end_first_half
END end_date1
FROM d2
You can use it in your query like below
Select * from enrollment_table a, (WITH data
AS (SELECT TRUNC(SYSDATE) curr_date from dual
),
d2
AS (SELECT curr_date,
To_date('0107'
||( Extract (year FROM curr_date) - 1 ), 'ddmmyyyy')
start_first_half,
To_date('3112'
||( Extract (year FROM curr_date) - 1 ), 'ddmmyyyy')
end_first_half,
To_date('0101'
||Extract (year FROM curr_date), 'ddmmyyyy')
start_second_half,
To_date('3006'
||Extract (year FROM curr_date), 'ddmmyyyy')
end_second_half
FROM data)
SELECT curr_date,
CASE
WHEN To_char(curr_date, 'MM') >= To_char(start_first_half, 'MM')
AND To_char(curr_date, 'MM') <= To_char(end_first_half, 'MM') THEN
start_second_half
ELSE start_first_half
END start_date1,
CASE
WHEN To_char(curr_date, 'MM') >= To_char(start_first_half, 'MM')
AND To_char(curr_date, 'MM') <= To_char(end_first_half, 'MM') THEN
end_second_half
ELSE end_first_half
END end_date1
FROM d2 ) b
where a.enrollment_date >=b.start_date1
and a.enrollment_date <=b.end_date1
I have written a query to extract hours between dates. This will basically retrieve the time entry for the employee. When I am executing the below query, It is working fine but when i add the extract function I am getting an error :
ORA-30076: invalid extract field for extract source
select emp_id, start_date , stop_date ,
(ROUND(SUM(
(EXTRACT (DAY FROM line_stop_time - line_start_time)*(3600*24))
+ (EXTRACT (HOUR FROM line_stop_time - line_start_time)*(3600))
+ (EXTRACT (MINUTE FROM line_stop_time - line_start_time)*(60))
+ (EXTRACT (SECOND FROM line_stop_time- line_start_time)*1)
) / (3600), 2) )recorded
from
(SELECT DISTINCT papf.person_number emp_id,
( To_char (sh21.start_time, 'DD-MM-YYYY') ) start_date,
( To_char (sh21.stop_time, 'DD-MM-YYYY') ) stop_date,
To_char(sh21.start_time, 'HH24:MI') line_start_time,
To_char(sh21.stop_time, 'HH24:MI') line_stop_time
FROM per_all_people_f papf,
per_all_assignments_m asg,
per_legal_employers ple,
hwm_tm_rec sh21,
hwm_tm_rec_grp_usages sh22,
hwm_tm_rec_grp sh23,
-- hwm_tm_rec_grp sh29,
hwm_grp_type sh24,
hwm_tm_rep_atrb_usages sh25,
hwm_tm_rep_atrbs sh26,
hwm_tm_statuses sh27,
hwm_tm_status_def_b sh28
WHERE papf.person_id = asg.person_id(+)
AND asg.legal_entity_id = ple.organization_id
AND asg.primary_flag = 'Y'
AND asg.assignment_status_type = 'ACTIVE'
AND ple.status = 'A'
AND Trunc (SYSDATE) BETWEEN papf.effective_start_date AND
papf.effective_end_date
AND Trunc (SYSDATE) BETWEEN asg.effective_start_date AND
asg.effective_end_date
AND Trunc (SYSDATE) BETWEEN ple.effective_start_date AND
ple.effective_end_date
AND papf.person_number = '55'
AND sh21.latest_version = 'Y'
AND sh21.resource_type = 'PERSON'
AND sh21.tm_rec_id = sh22.tm_rec_id
AND sh21.tm_rec_version = sh22.tm_rec_version
AND sh21.layer_code = 'TIME_RPTD'
AND sh22.layer_code = 'TIME_RPTD'
AND sh22.tm_rec_grp_id = sh23.tm_rec_grp_id
AND sh22.tm_rec_grp_version = sh23.tm_rec_grp_version
AND sh23.latest_version = 'Y'
AND sh21.resource_id = sh23.resource_id
AND sh23.grp_type_id = sh24.grp_type_id
--AND sh25.usages_source_id =sum.TM_REC_GRP_ID
AND sh21.tm_rec_id = sh25.usages_source_id
AND sh21.tm_rec_version = sh25.usages_source_version
AND sh25.usages_type = 'TIME_RECORD'
AND sh24.name = 'Processed TimecardDay'
AND sh25.tm_rep_atrb_id = sh26.tm_rep_atrb_id
AND sh26.attribute_category IN (SELECT base_element_name
FROM pay_element_types_f)
AND sh21.resource_id = papf.person_id
AND sh21.tm_rec_type IN ( 'RANGE', 'MEASURE' )
AND sh27.tm_status_def_id = sh28.tm_status_def_id
AND sh27.tm_bldg_blk_id = sh21.tm_rec_id
AND sh27.tm_bldg_blk_version = sh21.tm_rec_version
ORDER BY papf.person_number,
( To_char (sh21.start_time, 'DD-MM-YYYY') ) DESC)
group by emp_id, start_date , stop_date
How can i change the extract function to give me the same result. Is there an alternative for extract function to give the same results ?
Try converting your columns to date first to avoid error...:
EXTRACT (DAY FROM to_date(line_stop_time , 'dd-mm-yyyy'))
- EXTRACT (DAY FROM to_date(line_start_time, 'dd-mm-yyyy'))
Here is the DEMO.
And for the HOUR, MINUTE and SECOND use the format mask 'dd-mm-yyyy hh24:mi:ss'
Hope this will help!
You can easily find the hours between two date using:
select (end_date - start_date) * 24 from dual;
If your column is of type timestamp then use the following:
SELECT
EXTRACT(DAY FROM DIFF) * 24 +
EXTRACT(HOUR FROM DIFF) +
ROUND(EXTRACT(MINUTE FROM DIFF) / 60, 2) +
ROUND(EXTRACT(SECOND FROM DIFF) / 3600, 2) TOTAL_HOURS
FROM
(SELECT END_TIMESTAMP - START_TIMESTAMP AS DIFF
FROM DUAL)
-- or --
select (CAST(END_TIMESTAMP AS DATE)- CAST(START_TIMESTAMP AS DATE) ) * 24 from dual;
Cheers!!
Well,
Your LINE_STOP_TIME and LINE_START_TIME are character strings, not dates, so you cannot extract date info from them. Remove the TO_CHAR from the inline SELECT and it should be ok.
I have query in access db which looks like this:-
IIf(Weekday([HiredDate])=7,[HiredDate],[HiredDate]-Weekday([HiredDate])) AS TrainingStart,
Date()+6-Weekday(Date()) AS EndOfWeek,
DateDiff("ww",[TrainingStart],[EndOfWeek]) AS WeekNumber,
How do I write query with same logic in my oracle.
Here is what I have written and I do not think it is right.
SELECT Name, HiredDate,
(CASE WHEN (to_char(to_date(HiredDate), 'd') = 7) THEN HiredDate ELSE (HiredDate - to_char(to_date(HiredDate), 'd')) END) as TraingStart,
(CURRENT_DATE + (6 - to_char(to_date(CURRENT_DATE), 'd'))) as EndDate,
(To_Number(to_char(to_date((CURRENT_DATE + (6 - to_char(to_date(CURRENT_DATE), 'd')))),'WW')) - To_number(to_char(to_date((HiredDate - to_char(to_date(HiredDate), 'd'))),'WW')) +1) as WEEKNUMBER
FROM employee;
I always use something like following to count weeks as Change in the year of the start and end date can affect my result.
You can try the following:
WITH DATE_RANGE AS (
SELECT
DATE '2019-12-01' START_DATE,
DATE '2020-02-26' END_DATE
FROM
DUAL
)
SELECT
COUNT(LEVEL) AS NUMBER_OF_WEEKS
-- , LEVEL "Week",
-- TRUNC(START_DATE +(7 *(LEVEL - 1)), 'IW') AS START_OF_THE_WEEK,
-- TRUNC(START_DATE +(7 *(LEVEL - 1)), 'IW') + 6 AS END_OF_THE_WEEK,
-- TO_CHAR(START_DATE +(7 *(LEVEL - 1)), 'IW') WEEK_NUMBER
FROM
DATE_RANGE
CONNECT BY
LEVEL <= ( TRUNC(END_DATE, 'IW') - TRUNC(START_DATE, 'IW') ) / 7 + 1;
Output:
NUMBER_OF_WEEKS
---------------
14
Cheers!!
Try this to find number of weeks between 2 dates.
SELECT (trunc(TO_DATE(CURRENT_DATE, 'DD/MM/YYYY'), 'd')-trunc(TO_DATE(HIRED_DATE, 'DD/MM/YYYY'), 'd'))/7
FROM dual;
Oracle (SQL) - I have 3 available dates in a month (1st, 10th and 25th). I need a query to find out the closest among the 3 dates based on the date of executing my query. For e.g, when i run the query on 4th, i should get 10th as my result, when i run on 12th, the result should be 25th and when i run on 27th, the result should be the 01st of next month.
I am struggling with the logic. Please help..
with
inputs ( dt ) as (
select to_date( '03/24/2015 11:30:00', 'mm/dd/yyyy hh24:mi:ss') from dual union all
select to_date( '08/03/2016 07:15:00', 'mm/dd/yyyy hh24:mi:ss') from dual union all
select to_date( '02/29/2016 22:30:00', 'mm/dd/yyyy hh24:mi:ss') from dual
)
-- End of simulated inputs (for testing only, not part of the solution).
-- SQL query begins BELOW THIS LINE. Use your actual table and column names.
select dt,
case when extract(day from dt) < 10 then trunc(dt, 'mm') + interval '9' day
when extract(day from dt) < 25 then trunc(dt, 'mm') + interval '24' day
else add_months(trunc(dt, 'mm'), 1)
end as next_std_dt
from inputs;
DT NEXT_STD_DT
------------------- -------------------
03/24/2015 11:30:00 03/25/2015 00:00:00
08/03/2016 07:15:00 08/10/2016 00:00:00
02/29/2016 22:30:00 03/01/2016 00:00:00
I believe this is much more efficient and simpler than the other solutions.
WITH
possible_dates
AS
-- generate the three available dates for the current month
(SELECT TRUNC (SYSDATE, 'MM') available_date
FROM DUAL
UNION ALL
SELECT TRUNC (SYSDATE, 'MM') + 9
FROM DUAL
UNION ALL
SELECT TRUNC (SYSDATE, 'MM') + 24
FROM DUAL
UNION ALL
SELECT ADD_MONTHS (TRUNC (SYSDATE, 'MM'), 1)
FROM DUAL),
delta
AS
-- calculate the distance of those available dates
(SELECT (available_date - SYSDATE) diff, available_date
FROM possible_dates)
SELECT *
FROM delta
WHERE diff = (SELECT MIN (diff)
FROM delta
WHERE diff >= 0);
If using PL SQL is an option, then use the query as following:
`DECLARE
curr_month CHAR(2);
curr_year CHAR(4);
future_date DATE;
BEGIN
select to_char(sysdate, 'MM') INTO curr_month from dual;
select to_char(sysdate, 'YYYY') INTO curr_year from dual;
future_date := TO_DATE('12' || curr_month || curr_year, 'DD/MM/YYYY');
IF (SYSDATE > future_date) THEN
{..whatever you want to do...}
ELSIF (SYSDATE > future_date2) THEN
{..whatever you want to do...}
END IF;
END;`
WITH mytable(dt) AS
(SELECT '01'
FROM dual
UNION ALL SELECT '10'
FROM dual
UNION ALL SELECT '25'
FROM dual),
given_dates AS
(SELECT Trunc (To_date (dt || To_char(sysdate, 'MMYYYY'), 'DDMMYYYY')) dt,
Trunc(sysdate) cdate
FROM mytable),
comp AS
(SELECT cdate,
CASE
WHEN ABS (cdate - dt) < ABS (cdate - Add_months (dt, 1)) THEN dt
ELSE Add_months (dt, 1)
END dt_comp
FROM given_dates)
SELECT dt_comp closest_date
FROM
(SELECT dt_comp,
rank() OVER (
ORDER BY ABS (cdate - dt_comp)) rn
FROM comp)
WHERE rn = 1;