ORACLE Query Count Slow - oracle

I'm trying to query my Oracle script on Toad but got slow response, about 4-8 seconds.
The script query is about count, below is mine:
SELECT COUNT(*)
AS TOTALS
FROM(SELECT S.BADGEID_FK, S.SHIFT, S.STATUS, E.BADGEID, E.FIRSTNAME, E.LASTNAME
FROM WA_SEW_TBL_EMP_INFO S, WA_GA_TBL_EMPLOYEES E
WHERE S.BADGEID_FK = E.BADGEID AND S.STATUS = 'Attend' AND S.SHIFT = 'Morning'
AND S.BADGEID_FK NOT IN(SELECT EMPID
FROM WA_SEW_TBL_RESULTS
WHERE TO_CHAR(SYSTEM_DATE, 'YYYY-MM-DD') = '2017-08-30'
AND TO_CHAR(SYSTEM_DATE, 'HH24:MI') >= '07:00'
AND TO_CHAR(SYSTEM_DATE, 'HH24:MI') <= '19:29'))
I tried to add indexing to some column, but there is no effect.
Is there any way to do that query? or any trick?

This part:
WHERE TO_CHAR(SYSTEM_DATE, 'YYYY-MM-DD') = '2017-08-30'
AND TO_CHAR(SYSTEM_DATE, 'HH24:MI') >= '07:00'
AND TO_CHAR(SYSTEM_DATE, 'HH24:MI') <= '19:29'
Would be better rewritten as:
WHERE SYSTEM_DATE between to_date ('2017-08-30 07:00:00', 'YYYY-MM-DD HH24:MI:SS')
and to_date ('2017-08-30 19:29:59', 'YYYY-MM-DD HH24:MI:SS')
That will allow any index on SYSTEM_DATE to be used.

One obvious suspect is your date manipulation in the IN list. You should never, ever use functions around dates - that kills any ability of Oracle to use an index on the date column.
Instead:
where system_date >= to_date('2017-08-30 07:00', 'yyyy-mm-dd hh24:mi')
and system_date < to_date('2017-08-30 19:30', 'yyyy-mm-dd hh24:mi')
(the second inequality is strict, if you want to exclude 7:30pm sharp).

I was able to eliminate most of the subqueries but I'm not sure it will result in the performance gain w/o knowledge of table size and indexes. Posting the execution plan would help us understand where your bottleneck is.
SELECT count(*) as Totals
FROM WA_SEW_TBL_EMP_INFO S
INNER JOIN WA_GA_TBL_EMPLOYEES E
ON S.BADGEID_FK = E.BADGEID
LEFT JOIN WA_SEW_TBL_RESULTS R
ON S.BADGEID_FK =R.EMPID
-- Others already addressed what needs to happen here.
AND TO_CHAR(R.SYSTEM_DATE, 'YYYY-MM-DD') = '2017-08-30'
AND TO_CHAR(R.SYSTEM_DATE,'HH24:MI') >= '07:00'
AND TO_CHAR(R.SYSTEM_DATE,'HH24:MI') <= '19:29'
WHERE S.STATUS = 'Attend'
AND S.SHIFT = 'Morning'
AND R.EmpID is null

Related

First_Day/Last_Day Query

Could you please help me to correct the script as below:
set LastDay=SELECT LAST_DAY(SYSDATE) FROM dual;
set FirstDay=Select trunc((sysdate),'month') as First_day_of_month from dual;
SELECT count(*) FROM tab1 g , h.ab1 LEFT JOIN tab2 h ON g.bba = h.bba
WHERE 1 = 1
AND g.DATE_ BETWEEN TO_DATE('FirstDay', 'YYYYMMDD') AND TO_DATE('LastDay', 'YYYYMMDD');
In Oracle, a DATE ALWAYS has year, month, day, hour, minute and second components. Using LAST_DAY(SYSDATE) only sets the year-month-day component of a date and does not modify the time component so if you filter from the start of the month to LAST_DAY(SYSDATE) then you will exclude any values from the last day of the month with a time component between the current time and 23:59:59.
What you want is to use:
SELECT count(*)
FROM tab1 g
CROSS JOIN h.ab1 -- your query is confusing around the joins
-- and may need fixing
LEFT JOIN tab2 h
ON g.bba = h.bba
WHERE g.DATE_ >= TRUNC(SYSDATE, 'MM')
AND g.DATE_ < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);
That's just
select count(*)
from tab1 g left join tab2 h on g.bba = h.bba
and g.date_ between trunc(sysdate, 'month') and last_day(sysdate);
Isn't it?

How to compare system month and sales months?

I want to compare sales months with the system month. if equal get 1 output as new column(last_month) else 0. I tried below oracle query but I am getting a Null value.
SELECT a.agent_id,a.agent_name,a.ivr_registered_district,s.agent_type,s.district,s.province,a.parent_level1_id,a.parent_level1,a.sales_channel,TO_CHAR(TRUNC(a.connection_date, 'MONTH'), 'MON-YYYY') AS MONTHYEAR,
CASE
WHEN TO_CHAR(TRUNC(a.connection_date, 'MONTH'), 'MON-YYYY') = TO_CHAR(ADD_MONTHS(SYSDATE, - 1))
THEN 1
END as last_month
FROM EDW_TGT.FACT_LTE_SALES_CHANNELS a
JOIN SFA.sfa_agent_dtl s
ON a.agent_id = s.agent_id
where a.pre_post = 'LTE-PREPAID' and a.sales_channel in ('BUSINESS PARTNER', 'INSTITUSIONAL', 'REGIONAL TRADE PARTNERS', 'DISTRIBUTOR')
and a.connection_date >= TO_DATE('2020-01-01 00:00:0', 'YYYY-MM-DD HH24:MI:SS')
You don't need to truncate date as oracle provides you functionality to extract (day,month,year) from to_date function.
Your query should be like below.
SELECT a.agent_id,a.agent_name,a.ivr_registered_district,s.agent_type,s.district,s.province,a.parent_level1_id,a.parent_level1,a.sales_channel,TO_CHAR(TRUNC(a.connection_date, 'MONTH'), 'MON-YYYY') AS MONTHYEAR,
CASE
WHEN to_char(to_date(a.connection_date, 'DD-MM-YYYY'), 'Month') = to_char(to_date(sysdate-1, 'DD-MM-YYYY'), 'Month')
THEN 1
Else 0
END as last_month
FROM EDW_TGT.FACT_LTE_SALES_CHANNELS a
JOIN SFA.sfa_agent_dtl s
ON a.agent_id = s.agent_id
where a.pre_post = 'LTE-PREPAID' and a.sales_channel in ('BUSINESS PARTNER', 'INSTITUSIONAL', 'REGIONAL TRADE PARTNERS', 'DISTRIBUTOR')
and a.connection_date >= TO_DATE('2020-01-01 00:00:0', 'YYYY-MM-DD HH24:MI:SS')

ORACLE Query Where condition based on system time

I have an Oracle query below:
select ltrim(ROUND((1-n/c)*100) || '%') as TOTAL
from (select count(*) c
from WA_SEW_TBL_EMP_INFO
WHERE SHIFT = 'Morning')
, (SELECT COUNT(*) n
FROM (SELECT S.BADGEID_FK
FROM WA_SEW_TBL_EMP_INFO S
, WA_GA_TBL_EMPLOYEES E
WHERE S.BADGEID_FK = E.BADGEID
AND S.STATUS = 'Attend'
AND S.SHIFT = 'Morning'
AND S.BADGEID_FK NOT IN ( SELECT EMPID
FROM WA_SEW_TBL_RESULTS
WHERE SYSTEM_DATE between to_date ('2017-08-31 07:00', 'YYYY-MM-DD HH24:MI')
and to_date ('2017-08-31 19:29', 'YYYY-MM-DD HH24:MI')
)
)
)
Shift have 2 type:
Night
Morning
Now I want oracle query detected if system time from 08:00 until 19:29 then set shift to be Morning, else Night.
means I want WHERE SHIFT = 'system time condition from08:00until19:29then set shift to be Morning, else Night'
Is it possible to do that?
This query uses the 'sssss' date mask which is the number of seconds past midnight. Then you can tell whether the time element is within your desired range.
SELECT EMPID,
case when to_number(to_char(system_date, 'sssss'))
between 28800 -- 08:00:00
and 70140 -- 19:29:00
then 'Morning'
else 'Night' as SHIFT
FROM WA_SEW_TBL_RESULTS
where trunc(system_date) = date '2017-08-31'
Inject this sub-query into your main query as you need.
Incidentally I have closed the date range with a bound of seconds for 19:29:00 as you specified. Maybe that should be 70199, to take the day shift up to 19:29:59 - it depends how precisely you track time.
I think you can work like this:
WITH TEST_DATETIME AS(
SELECT TO_DATE('2017/08/31 10:15:24','YYYY/MM/DD HH24:MI:SS') DT FROM DUAL
UNION ALL
SELECT TO_DATE('2017/08/31 19:40:24','YYYY/MM/DD HH24:MI:SS') FROM DUAL
UNION ALL
SELECT SYSDATE FROM DUAL
)
SELECT CASE WHEN to_char(DT, 'hh24:mi:ss') > '08:00:00' AND to_char(DT, 'hh24:mi:ss') < '19:29:00' THEN 'Morning' ELSE 'Night' END AS NOON
FROM TEST_DATETIME
Easiest way is to write function returning day/night shift and include it in your query
create or replace function find_shift(dat in date) return varchar2
is
dat2 date;
treshhold1 date;
treshhold2 date;
ret varchar2(10);
begin
dat2:=to_date(to_char(dat, 'HH24:MI'),'HH24:MI');
treshhold1 := to_date('08:00','HH24:MI');
treshhold2 := to_date('19:29','HH24:MI');
if dat2>=treshhold1 and dat2<= treshhold2 then
ret := 'Morning';
else
ret := 'Night';
end if;
return ret
end;

oracle olap query execution is taking too long

I have these following tables:
1) date_table_dim
2) clock_table_dim
3) onlinegpspoint : which contains our main information for olap reports
And also there is a sql query like this:
SELECT
date_table_dim.day_id day_id,
clock_table_dim.hour_id hour_id
FROM onlinegpspoint olgps
INNER JOIN date_table_dim
ON (
olgps.occurance_time >= to_date('2014-03-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
AND olgps.occurance_time >= date_table_dim.day_id
AND olgps.occurance_time < date_table_dim.day_id + 1
)
INNER JOIN clock_table_dim
ON ( clock_table_dim.hour_id <= TO_NUMBER(TO_CHAR(occurance_time, 'HH24'))
AND clock_table_dim.hour_id > TO_NUMBER(TO_CHAR((occurance_time - 1/24), 'HH24') ))
GROUP BY
date_table_dim.day_id,
clock_table_dim.hour_id ;
My problem is that this query is taking too long to execute.
What actions can be taken to improve the performance of this query execution?
EDIT
There is an index for occurance_time on onlinegpspoint. By this query I want to get some Olap information for 1 hour period of time. (This query is a kind of summary of my fact_table query.)
You can try following query.
SQLFiddle
with t as (select d.day_id, o.occurance_time ot
from onlinegpspoint o
join date_table_dim d on ( o.occurance_time >= date '2014-03-01'
and d.day_id <= o.occurance_time and o.occurance_time < d.day_id + 1) )
select day_id, c.hour_id
from t join clock_table_dim c on (
c.hour_id <= to_char(t.ot, 'HH24') and to_char((t.ot - 1/24), 'HH24') < c.hour_id )
group by day_id, c.hour_id order by day_id, c.hour_id;
In your original query you compare to_char(occurance_time, 'HH24') with hour_id, here index probably does not work.
So idea is to firstly filter data to interesting period and then work only with these filtered data.
There is one more query worth trying, which gave me promising results:
select distinct trunc(occurance_time) day_id, to_char(occurance_time, 'hh24')+0 hour_id
from onlinegpspoint o join (
select to_date(to_char(day_id, 'yyyy-mm-dd ')||' '
||lpad(hour_id, 2, 0), 'yyyy-mm-dd hh24') dt
from date_table_dim, clock_table_dim) d
on (o.occurance_time >= date '2014-03-01'
and d.dt-1/24 <= o.occurance_time and o.occurance_time < d.dt)

Query transactions that occured only at a certain time

I am trying to run a select query that will pull all meds scheduled at 2300 for a date range. Is there a way I can convert the scheduled date/time to just hour? This is what I have so far:
SELECT DISTINCT USERCODE,
TRANSACTIONID,
ACTION,
TRANSACTIONHOUR,
SOURCE,
RXNUMBER,
DESCRIPTION,
MASTERPATIENTID,
FACILITYCODE,
ADMINISTRATIONTIME
FROM ( ABC.TL TL
INNER JOIN
ABC.S_VIEW S_VIEW
ON (TL.RXNUMBER = S_VIEW.RXNUMBER))
INNER JOIN
ABC.PV PATIENTVISIT
ON (TL.MASTERPATIENTID = PV.MASTERPATIENTID)
WHERE (TL.USERCODE NOT IN ('ABC'))
AND (TL.ACTION IN ('A', 'DC'))
AND (TL.TRANSACTIONHOUR BETWEEN to_date('2011-07-01 00:00:00', 'yyyy/mm/dd hh24:mi:ss') AND to_date('2011-09-30 23:59:59', 'yyyy/mm/dd hh24:mi:ss')
I would like the query to include all dispense during the specified dates but only at 2300 time. Database is oracle 10g.
First, you have given the date string in a different format and the format in different format. Make it consistent.
Try this:
SELECT DISTINCT USERCODE,
TRANSACTIONID,
ACTION,
TRANSACTIONHOUR,
SOURCE,
RXNUMBER,
DESCRIPTION,
MASTERPATIENTID,
FACILITYCODE,
ADMINISTRATIONTIME
FROM ( ABC.TL TL
INNER JOIN
ABC.S_VIEW S_VIEW
ON (TL.RXNUMBER = S_VIEW.RXNUMBER))
INNER JOIN
ABC.PV PATIENTVISIT
ON (TL.MASTERPATIENTID = PV.MASTERPATIENTID)
WHERE (TL.USERCODE NOT IN ('ABC'))
AND (TL.ACTION IN ('A', 'DC'))
AND (TL.TRANSACTIONHOUR BETWEEN to_date('2011/07/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss') AND to_date('2011/09/30 23:59:59', 'yyyy/mm/dd hh24:mi:ss')
AND TO_CHAR(TL.TRANSACTIONHOUR, 'HH24MI') = '2300' --THIS IS THE NEW CONDITION

Resources