Oracle query optimization help (involves TRUNC) - oracle

Here is the query.
closed_ts is a timestamp column. What I am trying to do is, find out how many were "closed" today, this month, this year and this week. Is there a better way to do this?
select table_id,
case
when trunc(closed_ts) = trunc(SYSDATE, 'DD') then 1
else 0
end as day_count,
case
when trunc(closed_ts) >= trunc(SYSDATE, 'MM') then 1
else 0
end as month_count,
case
when trunc(closed_ts) >= trunc(sysdate, 'YYYY') then 1
else 0
end as year_count,
case
when trunc(closed_ts) >= TRUNC(sysdate, 'IW') then 1
else 0
end as week_count
from myTable

Seems like you would want to aggregate the values with a sum() around each case statement and group by table_id, but since you asked about query optimization...
If there is an index on closed_ts you won't be able to use it because you are trunc'ing it. Truncating the closed_ts date is unnecessary since you are checking whether it is strictly larger than day, week, month, year, so this query will definitely result in a full table scan.
I'd likely rewrite it without the trunc whether there was an index on closed_ts or not:
select table_id
,sum(case when closed_ts >= trunc(sysdate) then 1 else 0 end) as day_count
,sum(case when closed_ts >= trunc(sysdate,'MM') then 1 else 0 end) as month_count
,sum(case when closed_ts >= trunc(sysdate,'YYYY') then 1 else 0 end) as year_count
,sum(case when closed_ts >= trunc(sysdate,'IW') then 1 else 0 end) as week_count
from myTable
group by table_id
btw: table_id sounds like a primary or surrogate key - are you sure you want it in the results?

Pop an index on (closed_ts,table_id), and apply a predicate on closed_ts as below ...
select
table_id,
sum(case when closed_ts >= trunc(SYSDATE,'DD' ) then 1 end) day_count,
sum(case when closed_ts >= trunc(SYSDATE,'MM' ) then 1 end) month_count,
sum(case when closed_ts >= trunc(SYSDATE,'YYYY') then 1 end) year_count,
sum(case when closed_ts >= trunc(SYSDATE,'IW' ) then 1 end) week_count,
from
myTable
where
closed_ts >= least(trunc(SYSDATE,'YYYY'),trunc(SYSDATE,'IW'))
group by
table_id
As user1842757 mentioned, lose the trunc on the closed_ts. You can also lose the ELSE 0 in the case statements

Related

Performance of using cursor PLSQL

I have to get a count using a cursor.
the basic cursor is
get *
from table
In here there are three types of data I want to get the count of.
sysdate < start_date
sysdate > startdate and sysdate < enddate
sysdate > enddate
I have 2 ways of doing this.
Loop the above cursor and use a if condition to get the counts.
Create 3 separate cursors with the conditions and directly get the count
As I have a lot of data what way would be good in perspective of performance ?
Don't use a cursor at all.
Use conditional aggregation. That's almost surely faster than any cursor based approach.
SELECT count(CASE
WHEN sysdate < start_date THEN
1
END) count1,
count(CASE
WHEN sysdate > startdate
AND sysdate < enddate THEN
1
END) count2,
count(CASE
WHEN sysdate > enddate THEN
1
END) count3
FROM elbat;

Count and Sum inside a select with multiple sums and counts

I'm using sub-query factoring and I have a query that returns invoice lines, and in the end I have this final sub-query:
I've already tried Partition but without success
SELECT
COUNT(CASE WHEN PC <> 0 THEN 1 END) AS A_LINECOUNT,
SUM(CASE WHEN PC > 0 THEN NR ELSE 0 END) AS B_PRODUCTCOUNT,
COUNT(CASE WHEN ALLOW_PAY = 1 THEN 1 END) AS C_INVOICECOUNT, --- ERROR
SUM(CASE WHEN ALLOW_PAY = 1 THEN MISSING_VALUE ELSE 0 END) AS D_INVOICETOTAL, --- ERROR
COUNT(CASE WHEN IS_NON_LIQUIDABLE_PRODUCT = 1 THEN 1 END) AS E_CONDITIONCOUNT,
COUNT(CASE WHEN IS_LIQUIDABLE_PRODUCT = 1 THEN 1 END) AS F_CONDITIONCOUNT
FROM MAIN_Q
The calculation of C_INVOICECOUNT and D_INVOICETOTAL is not correct because their values are repeated within each line of the invoice. Please consider that um MAIN_Q i also have a document_id where i can group by.
thanks
Maybe I understood correctly, maybe not, but this is too long for comment. If yes, C_INVOICECOUNT can be count as:
count(distinct case when allow_pay = 1 then document_id end)
But the problem is with D_INVOICETOTAL. You have repeated values for each invoice here and details which do not repeat. If so, add row numbering to your query:
select main_q.*, row_number() over (partition by document_id) rn from main_q
and then in problematic places use rn = 1:
select ...
count(case when rn = 1 and allow_pay = 1 then 1 end),
sum(case when rn = 1 and allow_pay = 1 then missing_value else 0 end)
...
from (select main_q.*, row_number() over (partition by document_id) rn from main_q)
Only first rows for each invoice will be analysed. Of course you can add row_number in earlier step.

Oracle sqlplus query

I am getting "Invalid Number"
Can anyone please guide me what I am doing wrong here ???
select TO_CHAR(a.START_TIME,'YY-MON-DD HH24') as START_TIME,
count(*) AS NbOperations,
SUM(CASE WHEN OPERATION_RESULT=0 THEN 1 ELSE 0 END) AS Success,
SUM(CASE WHEN OPERATION_RESULT=200 THEN 1 ELSE 0 END) AS Cancel,
SUM(CASE WHEN OPERATION_RESULT=203 THEN 1 ELSE 0 END) AS AppletInternalError,
SUM(CASE WHEN OPERATION_RESULT=406 THEN 1 ELSE 0 END) AS TimeoutWaitForCard,
SUM(CASE WHEN OPERATION_RESULT=413 THEN 1 ELSE 0 END) AS BillingError,
SUM(CASE WHEN OPERATION_RESULT=1000 THEN 1 ELSE 0 END) AS ABANDONNED,
SUM(CASE WHEN OPERATION_RESULT=1004 THEN 1 ELSE 0 END) AS ABANDON_FOR_NEW_OPERATION,
SUM(CASE WHEN OPERATION_RESULT NOT IN (0,200,203,406,413,1000,1004) THEN 1 ELSE 0 END) AS NbOthers
from MyTable1 a,
MyTable2 b
where OPERATION_TYPE in (2,3,4,5)
and a.OPERATION_TYPE=b.OPERATION_ID
and a.START_TIME >= to_timestamp('&1', 'YYYY-MM')
and a.START_TIME < to_timestamp('&2', 'YYYY-MM') + interval '1' month
group by TO_CHAR(a.START_TIME,'YY-MON-DD HH24')
order by TO_CHAR(START_TIME,'YY-MON-DD HH24');
check the format of your inputs &1 and &2 maybe it doesn't match with your date mask: 'YYYY-MM'. Another possible reason is the contents of the table: OPERATION_TYPE and OPERATION_RESULT are both number datatypes? Possible there are storing things different from a number.
I am able to solve the issue using below SQL code:
select TO_CHAR(a.START_TIME,'YY-MON-DD HH24') as Hourly, count(*) AS NbOperations,
SUM(CASE WHEN OPERATION_RESULT=0 THEN 1 ELSE 0 END) AS Success,
SUM(CASE WHEN OPERATION_RESULT=200 THEN 1 ELSE 0 END) AS Cancel,
SUM(CASE WHEN OPERATION_RESULT=203 THEN 1 ELSE 0 END) AS AppletInternalError,
SUM(CASE WHEN OPERATION_RESULT=406 THEN 1 ELSE 0 END) AS TimeoutWaitForCard,
SUM(CASE WHEN OPERATION_RESULT=413 THEN 1 ELSE 0 END) AS BillingError,
SUM(CASE WHEN OPERATION_RESULT=1000 THEN 1 ELSE 0 END) AS ABANDONNED,
SUM(CASE WHEN OPERATION_RESULT=1004 THEN 1 ELSE 0 END) AS ABANDON_FOR_NEW_OPERATION,
SUM(CASE WHEN OPERATION_RESULT NOT IN (0,200,203,406,413,1000,1004) THEN 1 ELSE 0 END) AS NbOthers
from gsg3_invocation_history a, GSG3_OPERATION_TYPE b where START_TIME >= to_timestamp('&1', 'YYYY-MM')
and START_TIME < to_timestamp('&2', 'YYYY-MM') + interval '1' month and OPERATION_TYPE in (2,3,4,5) and a.OPERATION_TYPE=b.OPERATION_ID
group by TO_CHAR(a.START_TIME,'YY-MON-DD HH24') order by TO_CHAR(START_TIME,'YY-MON-DD HH24');

Missing Right Parenthesis error while executing this query in oracle

I am executing this query in oracle and it is giving me an error of missing right parenthesis at line 33. Is there anyone who can help me resolve this issue. Thank you
Here is my query
WITH t AS (
SELECT RM_LIVE.EMPLOYEE.EMPNO,
RM_LIVE.EMPNAME.FIRSTNAME,
RM_LIVE.EMPNAME.LASTNAME,
RM_LIVE.CRWBASE.BASE,
RM_LIVE.CRWCAT.crwcat,
RM_LIVE.CRWSPECFUNC.IDCRWSPECFUNC
FROM RM_LIVE.EMPBASE,
RM_LIVE.EMPLOYEE,
RM_LIVE.CRWBASE,
RM_LIVE.EMPNAME,
RM_LIVE.CRWSPECFUNC,
RM_LIVE.EMPSPECFUNC,
RM_LIVE.EMPQUALCAT,
RM_LIVE.CRWCAT
where RM_LIVE.EMPBASE.IDEMPNO = RM_LIVE.EMPLOYEE.IDEMPNO
AND RM_LIVE.EMPBASE.IDCRWBASE = RM_LIVE.CRWBASE.IDCRWBASE
AND RM_LIVE.EMPLOYEE.IDEMPNO = RM_LIVE.EMPNAME.IDEMPNO
AND RM_LIVE.EMPSPECFUNC.IDCRWSPECFUNC =RM_LIVE.CRWSPECFUNC.IDCRWSPECFUNC
AND RM_LIVE.EMPSPECFUNC.IDEMPNO =RM_LIVE.EMPLOYEE.IDEMPNO
AND RM_LIVE.EMPQUALCAT.IDEMPNO=RM_LIVE.EMPLOYEE.IDEMPNO
AND RM_LIVE.CRWCAT.IDCRWCAT = RM_LIVE.EMPQUALCAT.IDCRWCAT
AND RM_LIVE.CRWCAT.crwcat IN ('CP','FO','CM','MC')
AND RM_LIVE.CRWBASE.BASE <> 'XYZ'
AND RM_LIVE.CRWSPECFUNC.IDCRWSPECFUNC IN
('921','2' ,'1','301','17','4','3','7','302' ,'861','31',
'723','30','722 ','29 ','721','16','601','581')
AND RM_LIVE.EMPBASE.STARTDATE <= SYSDATE
AND RM_LIVE.EMPBASE.ENDDATE >= SYSDATE
AND RM_LIVE.EMPSPECFUNC.STARTDATE <= SYSDATE
AND RM_LIVE.EMPSPECFUNC.ENDDATE >= SYSDATE
AND RM_LIVE.EMPNAME.FROMDATE <=SYSDATE
AND RM_LIVE.EMPQUALCAT.STARTDATE <= SYSDATE
AND RM_LIVE.EMPQUALCAT.ENDDATE >= SYSDATE AS ta (EMPNO,EMPFIRSTNAME,EMPLASTNAME, Base, CAT, code)
)
SELECT DISTINCT
t.EMPNO,
t.EMPFIRSTNAME,
t.EMPLASTNAME,
t.Base,
t.CAT,
(ABS(oa.val1) * NVL(NULLIF((ABS(oa.val2) * ABS(oa.val3)),0),1) * ABS(oa.val4) * ABS(oa.val5) * ABS(oa.val6) * ABS(oa.val7) * ABS(oa.val8) * ABS(oa.val9)) AS "FTE VALUE"
FROM t
OUTER APPLY (SELECT MAX(CASE WHEN t2.code IN (1,2,4) THEN 0.70 ELSE -1 END) AS val1,
MAX(CASE WHEN t2.code IN (1,2) THEN 0 ELSE -1 END) AS val2,
MAX(CASE WHEN t2.code IN (4) THEN 1.29 ELSE -1 END) AS val3,
MAX(CASE WHEN t2.code IN ( 861 ) THEN 0.80 ELSE -1 END) AS val4
MAX(CASE WHEN t2.code IN (921,301,30,722,601,581) THEN 0.50 ELSE -1 END) AS val5
MAX(CASE WHEN t2.code IN (17,302,16) THEN 0.85 ELSE -1 END) AS val6
MAX(CASE WHEN t2.code IN (29,721) THEN 0.25 ELSE -1 END) AS val7
MAX(CASE WHEN t2.code IN (31,723) THEN 0.75 ELSE -1 END) AS val8
MAX(CASE WHEN t2.code IN (3,7) THEN 0.90 ELSE -1 END) AS val9
FROM t AS t2 WHERE t2.EMPNO = t.EMPNO) oa
The last line of your sub-query factoring (WITH ... AS ( ... )) clause is:
AND RM_LIVE.EMPQUALCAT.ENDDATE >= SYSDATE AS ta (EMPNO,EMPFIRSTNAME,EMPLASTNAME, Base, CAT, code)
The AS ta (...) is invalid syntax.
If you want to name the columns then you need to delete that part and change the first line to:
WITH t (EMPNO,EMPFIRSTNAME,EMPLASTNAME, Base, CAT, code) AS (
However, that is syntax introduced in Oracle 11g and won't work in Oracle 10g - if you want to support that version (and it seems you do since you've tagged it) then just explicitly alias each column:
WITH t AS (
SELECT RM_LIVE.EMPLOYEE.EMPNO,
RM_LIVE.EMPNAME.FIRSTNAME AS EMPFIRSTNAME,
RM_LIVE.EMPNAME.LASTNAME AS EMPLASTNAME,
RM_LIVE.CRWBASE.BASE,
RM_LIVE.CRWCAT.crwcat AS CAT,
RM_LIVE.CRWSPECFUNC.IDCRWSPECFUNC AS CODE

How do I sum records by week using pl/sql?

I have a query in Oracle for a report that looks like this:
SELECT TRUNC (created_dt) created_dt
, COUNT ( * ) AllClaims
, SUM(CASE WHEN filmth_cd in ('T', 'C') THEN 1 ELSE 0 END ) CT
, SUM(CASE WHEN filmth_cd = 'W' THEN 1 ELSE 0 END ) Web
, SUM(CASE WHEN filmth_cd = 'I' THEN 1 ELSE 0 END ) Icon
FROM claims c
WHERE c.clsts_cd NOT IN ('IN', 'WD')
AND TRUNC (created_dt) between
to_date('1/1/2006', 'dd/mm/yyyy') AND
to_date('1/1/2100', 'dd/mm/yyyy')
GROUP BY TRUNC (created_dt)
ORDER BY TRUNC (created_dt) DESC;
It returns data like this:
Create_Dt AllClaims CT Web Icon
1/26/2011 675 356 285 34
1/25/2011 740 322 379 39
...
What I need is a result set that sums all of the daily values into a weekly value. I am pretty new to PL/SQL and not sure where to begin.
Something like
SELECT TRUNC (created_dt, 'IW') created_dt
, COUNT ( * ) AllClaims
, SUM(CASE WHEN filmth_cd in ('T', 'C') THEN 1 ELSE 0 END ) CT
, SUM(CASE WHEN filmth_cd = 'W' THEN 1 ELSE 0 END ) Web
, SUM(CASE WHEN filmth_cd = 'I' THEN 1 ELSE 0 END ) Icon
FROM claims c
WHERE c.clsts_cd NOT IN ('IN', 'WD')
AND TRUNC (created_dt) between
to_date('1/1/2006', 'dd/mm/yyyy') AND
to_date('1/1/2100', 'dd/mm/yyyy')
GROUP BY TRUNC (created_dt, 'IW')
ORDER BY TRUNC (created_dt, 'IW') DESC;
will aggregate the data based on the first day of the ISO week.
Here is a simple example that I quite often use:
SELECT
TO_CHAR(created_dt,'WW'),
max(created_dt),
COUNT(*)
from MY_TABLE
group by
TO_CHAR(created_dt,'WW');
The to_char(created_dt,'WW') create the groups, and the max(created_dt) displays the last day of the week. Use min(created_dt) if you want to display the first day of the week.

Resources