We have a pretty big plpgsql function with an if- and elsif-statement in PostgreSQL 9.4.4
Inside every if-body there are function calls to stable-sql functions.
We call the function in the following way:
SELECT *
from rawdata.getNumbersForUserBasedMetricEventsGroupedByClient('2015-09-28','2015-10-28','{4}'::int[],2,null,null,null,null,null);
The first 4-5 times the function executes quite fast in a about 2.5 seconds, but then suddenly the performance drops rapidly and the execution takes about 7.5 seconds. It stays at that level for all consecutive calls.
We also tried to declare the plpgsql function as stable, but that did not help.
When we call one of the inner stable-sql functions directly, the executions always take about 2.5 seconds.
This is the Schema of the rawdata.metricevent table:
rawdata.metricevent (metriceventid bigint PRIMARY KEY,
metricevent integer,
client integer,
age integer,
country varchar(256),
userideventowner bigint,
contributoruserid bigint,
tournamentid bigint,
eventoccurtime timestamp,
iscounted boolean)
We have a btree index over the eventoccurtime column. Without the btree index the difference is even bigger, the execution sometimes finished in just a few seconds, but sometimes it lasts more than 100 seconds.
Now our questions are: Why is that? What is happening, when the plpgsql function is executed the 5th or 6th time, why is it suddenly taking so long? Btw, the CPU-Load also is very high for this queries.
We also analyzed the query with EXPLAIN ANALYZE and the query planner ALWAYS takes about 0.034ms, but the query execution differs from 2.5 seconds to 7.5 seconds. And it also never is anywhere in between, its either 2.5 seconds or 7.5 seconds.
These are the Main-pgpsql function that has the variable execution times and the stable-sql function below that have constant execution times.
CREATE OR REPLACE FUNCTION rawdata.getNumbersForUserBasedMetricEventsGroupedByClient(pFrom timestamp, pTo timestamp, pMetricEvent integer[], pTimeDomainType integer,
pCountry varchar(100),pAgeFrom integer,pAgeTo integer,pUserlanguage varchar(50),pTournamentlanguage varchar(50))
RETURNS TABLE(dfrom timestamp, x bigint, y bigint, xx bigint, yy bigint)
AS $$
BEGIN
IF pTimeDomainType = 1 THEN
--hours
RETURN QUERY
SELECT * FROM rawdata.getNumbersForUBMetricEventsGroupedByClientPerHours(pFrom,pTo,pMetricEvent,pCountry,pAgeFrom,pAgeTo,pUserLanguage,pTournamentLanguage);
ELSIF pTimeDomainType = 2 THEN
--days
RETURN QUERY
SELECT * FROM rawdata.getNumbersForUBMetricEventsGroupedByClientPerDays(pFrom,pTo,pMetricEvent,pCountry,pAgeFrom,pAgeTo,pUserLanguage,pTournamentLanguage);
ELSIF pTimeDomainType = 3 THEN
--week
RETURN QUERY
SELECT * FROM rawdata.getNumbersForUBMetricEventsGroupedByClientPerWeeks(pFrom,pTo,pMetricEvent,pCountry,pAgeFrom,pAgeTo,pUserLanguage,pTournamentLanguage);
ELSIF pTimeDomainType = 4 THEN
--month
RETURN QUERY
SELECT * FROM rawdata.getNumbersForUBMetricEventsGroupedByClientPerMonths(pFrom,pTo,pMetricEvent,pCountry,pAgeFrom,pAgeTo,pUserLanguage,pTournamentLanguage);
END IF;
END;
$$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION rawdata.getNumbersForUBMetricEventsGroupedByClientPerHours(pFrom timestamp, pTo timestamp, pMetricEvent integer[],
pCountry varchar(100),pAgeFrom integer,pAgeTo integer,pUserlanguage varchar(50),pTournamentlanguage varchar(50))
RETURNS TABLE(dfrom timestamp, x bigint, y bigint, xx bigint, yy bigint)
AS $$
SELECT hours timedomain,count(distinct em.userideventowner) as x,count(distinct ef.userideventowner) as y,count(distinct emh.userideventowner) as xx,count(distinct efh.userideventowner) as yy
FROM generate_series
( pFrom::timestamp
, pTo::timestamp + '23 hour'
, '1 hour'::interval) hours
LEFT JOIN rawdata.metricevent e1 ON e1.eventoccurtime >=pFrom
AND e1.eventoccurtime < pTo + '1 day'
AND (e1.metricevent = ANY (pMetricEvent))
AND (e1.country = pCountry OR pCountry is null)
AND (e1.age >= pAgeFrom OR pAgeFrom is null) AND (e1.age <= pAgeTo OR pAgeTo is null)
AND userideventowner >= 110
AND hours = date_trunc('hour',e1.eventoccurtime)
LEFT JOIN rawdata.userlanguage ul ON e1.userideventowner = ul.userideventowner
AND (ul.userlanguage = pUserLanguage OR pUserLanguage is null)
LEFT JOIN rawdata.metricevent ei ON e1.metriceventid = em.metriceventid AND ei.client=1
LEFT JOIN rawdata.metricevent ea ON e1.metriceventid = ef.metriceventid AND ea.client=2
LEFT JOIN rawdata.metricevent ew ON e1.metriceventid = emh.metriceventid AND ew.client=3
LEFT JOIN rawdata.metricevent eww ON e1.metriceventid = efh.metriceventid AND eww.client=4
GROUP BY hours
ORDER BY hours;
$$
LANGUAGE sql STABLE;
CREATE OR REPLACE FUNCTION rawdata.getNumbersForUBMetricEventsGroupedByClientPerDays(pFrom timestamp, pTo timestamp, pMetricEvent integer[],
pCountry varchar(100),pAgeFrom integer,pAgeTo integer,pUserlanguage varchar(50),pTournamentlanguage varchar(50))
RETURNS TABLE(dfrom timestamp, x bigint, y bigint, xx bigint, yy bigint)
AS $$
SELECT days timedomain,count(distinct em.userideventowner) as x,count(distinct ef.userideventowner) as y,count(distinct emh.userideventowner) as xx,count(distinct efh.userideventowner) as yy
FROM generate_series
( pFrom::timestamp
, pTo::timestamp
, '1 day'::interval) days
LEFT JOIN rawdata.metricevent e1 ON e1.eventoccurtime >=pFrom
AND e1.eventoccurtime < pTo + '1 day'
AND (e1.metricevent = ANY (pMetricEvent))
AND (e1.country = pCountry OR pCountry is null)
AND (e1.age >= pAgeFrom OR pAgeFrom is null) AND (e1.age <= pAgeTo OR pAgeTo is null)
AND userideventowner >= 110
AND days = date_trunc('day',e1.eventoccurtime)
LEFT JOIN rawdata.userlanguage ul ON e1.userideventowner = ul.userideventowner
AND (ul.userlanguage = pUserLanguage OR pUserLanguage is null)
LEFT JOIN rawdata.metricevent ei ON e1.metriceventid = em.metriceventid AND ei.client=1
LEFT JOIN rawdata.metricevent ea ON e1.metriceventid = ef.metriceventid AND ea.client=2
LEFT JOIN rawdata.metricevent ew ON e1.metriceventid = emh.metriceventid AND ew.client=3
LEFT JOIN rawdata.metricevent eww ON e1.metriceventid = efh.metriceventid AND eww.client=4
GROUP BY days
ORDER BY days;
$$
LANGUAGE sql STABLE;
CREATE OR REPLACE FUNCTION rawdata.getNumbersForUBMetricEventsGroupedByClientPerWeeks(pFrom timestamp, pTo timestamp, pMetricEvent integer[],
pCountry varchar(100),pAgeFrom integer,pAgeTo integer,pUserlanguage varchar(50),pTournamentlanguage varchar(50))
RETURNS TABLE(dfrom timestamp, x bigint, y bigint, xx bigint, yy bigint)
AS $$
SELECT min(days) timedomain,count(distinct em.userideventowner) as x,count(distinct ef.userideventowner) as y,count(distinct emh.userideventowner) as xx,count(distinct efh.userideventowner) as yy
FROM generate_series
( pFrom::timestamp
, pTo::timestamp
, '1 day'::interval) days
LEFT JOIN rawdata.metricevent e1 ON e1.eventoccurtime >=pFrom
AND e1.eventoccurtime < pTo + '1 day'
AND (e1.metricevent = ANY (pMetricEvent))
AND (e1.country = pCountry OR pCountry is null)
AND (e1.age >= pAgeFrom OR pAgeFrom is null) AND (e1.age <= pAgeTo OR pAgeTo is null)
AND userideventowner >= 110
AND days = date_trunc('day',e1.eventoccurtime)
LEFT JOIN rawdata.userlanguage ul ON e1.userideventowner = ul.userideventowner
AND (ul.userlanguage = pUserLanguage OR pUserLanguage is null)
LEFT JOIN rawdata.metricevent ei ON e1.metriceventid = em.metriceventid AND ei.client=1
LEFT JOIN rawdata.metricevent ea ON e1.metriceventid = ef.metriceventid AND ea.client=2
LEFT JOIN rawdata.metricevent ew ON e1.metriceventid = emh.metriceventid AND ew.client=3
LEFT JOIN rawdata.metricevent eww ON e1.metriceventid = efh.metriceventid AND eww.client=4
GROUP BY EXTRACT(WEEK FROM days)
ORDER BY 1;
$$
LANGUAGE sql STABLE;
CREATE OR REPLACE FUNCTION rawdata.getNumbersForUBMetricEventsGroupedByClientPerMonths(pFrom timestamp, pTo timestamp, pMetricEvent integer[],
pCountry varchar(100),pAgeFrom integer,pAgeTo integer,pUserlanguage varchar(50),pTournamentlanguage varchar(50))
RETURNS TABLE(dfrom timestamp, x bigint, y bigint, xx bigint, yy bigint)
AS $$
SELECT min(days) timedomain,count(distinct em.userideventowner) as x,count(distinct ef.userideventowner) as y,count(distinct emh.userideventowner) as xx,count(distinct efh.userideventowner) as yy
FROM generate_series
( pFrom::timestamp
, pTo::timestamp
, '1 day'::interval) days
LEFT JOIN rawdata.metricevent e1 ON e1.eventoccurtime >=pFrom
AND e1.eventoccurtime < pTo + '1 day'
AND (e1.metricevent = ANY (pMetricEvent))
AND (e1.country = pCountry OR pCountry is null)
AND (e1.age >= pAgeFrom OR pAgeFrom is null) AND (e1.age <= pAgeTo OR pAgeTo is null)
AND userideventowner >= 110
AND days = date_trunc('day',e1.eventoccurtime)
LEFT JOIN rawdata.userlanguage ul ON e1.userideventowner = ul.userideventowner
AND (ul.userlanguage = pUserLanguage OR pUserLanguage is null)LEFT JOIN rawdata.metricevent ei ON e1.metriceventid = em.metriceventid AND ei.client=1
LEFT JOIN rawdata.metricevent ea ON e1.metriceventid = ef.metriceventid AND ea.client=2
LEFT JOIN rawdata.metricevent ew ON e1.metriceventid = emh.metriceventid AND ew.client=3
LEFT JOIN rawdata.metricevent eww ON e1.metriceventid = efh.metriceventid AND eww.client=4
GROUP BY EXTRACT(MONTH FROM days)
ORDER BY 1;
$$
LANGUAGE sql STABLE;
Kind regards, Thomas
Related
I have this query:
SELECT TRUNC(date_added,'MM'), count(*)
FROM payments_log l, product p
WHERE l.amount > 0
AND l.product_id = p.product_id
AND p.subproduct_id = 238
AND TRUNC(l.date_added) BETWEEN TO_DATE('01012020','MMDDYYYY') AND TO_DATE('01012021','MMDDYYYY')
AND l.return_code = 1
GROUP BY TRUNC(date_added,'MM')
ORDER BY TRUNC(date_added,'MM');
In addition to the count, per month, I want a column that is the average each month, of the total......not sure how to do this in the same query.
To get:
I want a column that is the average each month, of the total
You appear to want to use the AVG analytic function over the entire range:
SELECT month,
cnt,
AVG( cnt ) OVER () AS avg_cnt
FROM (
SELECT TRUNC(date_added,'MM') AS month,
COUNT(*) AS cnt
FROM payments_log l
INNER JOIN product p
ON ( l.product_id = p.product_id )
WHERE l.amount > 0
AND p.subproduct_id = 238
AND l.date_added >= DATE '2020-01-01'
AND l.date_added < DATE '2021-01-01'
AND l.return_code = 1
GROUP BY TRUNC(date_added,'MM')
)
ORDER BY month
You also should use ANSI joins rather than the (confusing) legacy comma joins and can filter on the date_added column without needing the TRUNC function (which, if you do use it, would prevent Oracle from using an index on the date_added column and would require a function-based index on TRUNC( date_added )).
(Note: BETWEEN is inclusive so that you will include 2021-01-01 in your range rather than just those dates in 2020; I am assuming that you do not want this date but if you do then you can set the upper bound to l.date_added < DATE '2021-01-02'.)
If you want the number of counts per month as a fraction of the total number of counts (this is not an average) then, again, you want to use an analytic function:
SELECT month,
cnt,
cnt / SUM( cnt ) OVER () AS fraction_of_total_cnt
FROM (
SELECT TRUNC(date_added,'MM') AS month,
COUNT(*) AS cnt
FROM payments_log l
INNER JOIN product p
ON ( l.product_id = p.product_id )
WHERE l.amount > 0
AND p.subproduct_id = 238
AND l.date_added >= DATE '2020-01-01'
AND l.date_added < DATE '2021-01-01'
AND l.return_code = 1
GROUP BY TRUNC(date_added,'MM')
)
ORDER BY month
I've had this query running for over 2 years and just recently this began throwing the ORA-01427 single row sub-query returns multiple rows error. What's the best way to debug this when there are multiple sub-queries? Should I be adding a MAX statement to each subquery? I've tried switching some of the = operators to IN but that's not working and I'm not confident it would give me the correct results either.
Appreciate any insight or assistance this community can provide. I'm ripping my hair out working through this one!
SELECT
NVL(
(SELECT
'Y'
FROM
PER_ASSIGNMENT_SUPERVISORS_F
WHERE
MANAGER_ID = PAPF.PERSON_ID
AND MANAGER_TYPE ='LINE_MANAGER'
AND TRUNC(SYSDATE) BETWEEN EFFECTIVE_START_DATE AND EFFECTIVE_END_DATE
AND ROWNUM = 1),'N') MANAGER_FLAG,
PAAM.ASSIGNMENT_STATUS_TYPE,
PAAM.EMPLOYMENT_CATEGORY,
TO_CHAR(PPOS.DATE_START,'YYYY-MM-DD') AS HIRE_DATE,
PER_EXTRACT_UTILITY.GET_STANDARD_WORKING_HOURS(PAAM.ASSIGNMENT_ID,TRUNC(SYSDATE)) AS STANDARD_WORKING_HOURS,
TO_CHAR(PAAM.EFFECTIVE_START_DATE,'YYYY-MM-DD') AS EFFECTIVE_START_DATE,
(
SELECT
PJFVL.JOB_CODE
FROM
PER_ALL_ASSIGNMENTS_M PAAM1,
PER_JOBS_F_VL PJFVL
WHERE
PAAM1.PERSON_ID = PASF.MANAGER_ID
AND TRUNC(SYSDATE) BETWEEN PAAM1.EFFECTIVE_START_DATE AND PAAM1.EFFECTIVE_END_DATE
AND TRUNC(SYSDATE) BETWEEN PJFVL.EFFECTIVE_START_DATE AND PJFVL.EFFECTIVE_END_DATE
and PAAM1.ASSIGNMENT_STATUS_TYPE='ACTIVE'
AND PAAM1.ASSIGNMENT_TYPE = 'E'
AND PAAM1.effective_latest_change = 'Y'
AND PJFVL.JOB_ID = PAAM1.JOB_ID
)as Manager_job_code,
(
SELECT
HOIF.ORG_INFORMATION1
FROM
PER_ALL_ASSIGNMENTS_M PAAM2,
HR_ORGANIZATION_INFORMATION_F HOIF
WHERE
PAAM2.PERSON_ID = PASF.MANAGER_ID
AND HOIF.ORG_INFORMATION_CONTEXT = 'DEPT_DET'
and PAAM2.ASSIGNMENT_STATUS_TYPE='ACTIVE'
AND PAAM2.ASSIGNMENT_TYPE = 'E'
AND PAAM2.effective_latest_change = 'Y'
AND TRUNC(SYSDATE) BETWEEN PAAM2.EFFECTIVE_START_DATE AND PAAM2.EFFECTIVE_END_DATE
AND TRUNC(SYSDATE) BETWEEN HOIF.EFFECTIVE_START_DATE AND HOIF.EFFECTIVE_END_DATE
AND HOIF.ORGANIZATION_ID = PAAM2.ORGANIZATION_ID
) as Manager_dep_code,
(SELECT
COUNT(PPOS.PERIOD_OF_SERVICE_ID)
FROM
PER_PERIODS_OF_SERVICE PPOS
WHERE
1 = 1
AND PAAM.PERSON_ID = PPOS.PERSON_ID
) AS INACTIVE_WORKRELATIONSHIP,
PAPF.PERSON_NUMBER as SAMACCOUNTNAME,
(SELECT CSB.NAME FROM
CMP_SALARY CS,
CMP_SALARY_BASES CSB
WHERE
CS.ASSIGNMENT_ID = PAAM.ASSIGNMENT_ID
AND CS.SALARY_BASIS_ID = CSB.SALARY_BASIS_ID
and TRUNC(SYSDATE) BETWEEN CS.DATE_FROM AND CS.DATE_TO
) AS hourly_salary_Paid,
TO_CHAR(PP.DATE_OF_BIRTH,'YYYY-MM-DD') AS DOB,
PPNFV.LAST_NAME,
PPNFV.FIRST_NAME,
HLA.LOCATION_NAME as Location,
HLA.ADDRESS_LINE_1 AS LOC_ADDRESS_1,
HLA.ADDRESS_LINE_2 AS LOC_ADDRESS_2,
HLA.TOWN_OR_CITY AS City,
HLA.POSTAL_CODE AS ZIP_CODE,
HLA.REGION_2 AS STATE,
PPNFV.KNOWN_AS AS PREFERRED_NAME,
TRIM((PPNFV.KNOWN_AS||' '||PPNFV.LAST_NAME)) AS PREFERRED_NAME_LAST_NAME,
(SELECT
PPNFV.DISPLAY_NAME
FROM per_person_names_f_v PPNFV
WHERE 1 = 1
AND PPNFV.PERSON_ID = PASF.MANAGER_ID
AND PPNFV.NAME_TYPE='GLOBAL'
AND TRUNC(SYSDATE) BETWEEN PPNFV.EFFECTIVE_START_DATE AND PPNFV.EFFECTIVE_END_DATE
) AS MANAGER_NAME,
(SELECT
PAPF.PERSON_NUMBER
FROM PER_ALL_PEOPLE_F PAPF
WHERE PAPF.PERSON_ID = PASF.MANAGER_ID
AND TRUNC(SYSDATE) BETWEEN PAPF.EFFECTIVE_START_DATE AND PAPF.EFFECTIVE_END_DATE
) AS MANAGER_NUMBER,
HAOUFVL.NAME AS DEPARTMENT,
PAAM.ASSIGNMENT_NAME AS JOB_TILE,
PLE.NAME as Company,
PJLG.INFORMATION1 AS FLSA,
SUBSTR(PNI.NATIONAL_IDENTIFIER_NUMBER,-4) AS SSN_NUMBER,
PAAM.ASS_ATTRIBUTE1 as Officer_TITLE,
(
select
bu.name
from
hr_all_organization_units_f_vl bu
where 1 = 1
and paam.business_unit_id = bu.organization_id
and trunc(sysdate) between bu.effective_start_date and bu.effective_end_date
) as BUS_UNIT,
TO_CHAR (PPOS.ORIGINAL_DATE_OF_HIRE,'YYYY-MM-DD') AS ORIGINAL_DATE_OF_HIRE1,
(
CASE WHEN PPLF.SEX = 'F' THEN
'TRUE'
ELSE
'FALSE'
END
) AS GENDER,
(
CASE WHEN PJFFVL.JOB_FAMILY_NAME = 'Executive' THEN
'ELT'
ELSE
' '
END
) AS ELT_DESIGNATION,
HOIF.ORG_INFORMATION1 as DEPATMENT_CODE,
PJFV.JOB_CODE AS JOB_CODE,
PAF.ADDRESS_LINE_1 AS HOME_ADDRESS_LINE_1,
PAF.ADDRESS_LINE_2 AS HOME_ADDRESS_LINE_2,
PAF.TOWN_OR_CITY AS HOW_ADDRESS_CITY,
PAF.REGION_2 AS HOME_ADDRESS_STATE,
PAF.POSTAL_CODE AS HOME_ADRESS_ZIP_CODE,
PGFTL.NAME as Grade_level,
(SELECT
distinct (per_extract_utility.get_decoded_lookup('JOB_FUNCTION_CODE',PJF.JOB_FUNCTION_CODE))
FROM
PER_JOB_SECURED_LIST_V job WHERE TRUNC(SYSDATE) BETWEEN effective_start_date AND effective_end_date
) as JOB_FUNCTION,
pp.attribute1 as PER_NETWORKID,
(SELECT
PPNFV.attribute1
FROM PER_PERSONS PPNFV
WHERE 1 = 1
AND PPNFV.PERSON_ID = PASF.MANAGER_ID
) AS MANAGER_NETWORKID,
HOIF.ORG_INFORMATION2 AS REGION,
to_char(paam.ass_attribute_date1,'MM/DD/YYYY') as OfficerPromoDate
FROM
PER_ALL_ASSIGNMENTS_M PAAM,
PER_ASSIGNMENT_SUPERVISORS_F PASF,
PER_PERIODS_OF_SERVICE PPOS,
PER_ALL_PEOPLE_F PAPF,
PER_PERSONS PP,
per_person_names_f_v PPNFV,
HR_LOCATIONS_ALL HLA,
HR_ALL_ORGANIZATION_UNITS_F_VL HAOUFVL,
PER_JOB_LEG_F PJLG,
PER_NATIONAL_IDENTIFIERS PNI,
PER_PEOPLE_LEGISLATIVE_F PPLF,
PER_JOB_FAMILY_F_VL PJFFVL,
PER_JOBS_F_V PJFV,
HR_ORGANIZATION_INFORMATION_F HOIF,
PER_ADDRESSES_F PAF,
PER_PERSON_ADDR_USAGES_F PPAUF,
PER_GRADES_F_TL PGFTL,
PER_JOBS_F PJF,
PER_LEGAL_EMPLOYERS PLE,
HR_ALL_ORGANIZATION_UNITS_F HAOUF
WHERE
PAAM.ASSIGNMENT_STATUS_TYPE='ACTIVE'
AND PAAM.ASSIGNMENT_TYPE = 'E'
AND PAAM.effective_latest_change = 'Y'
AND PAAM.ASSIGNMENT_ID = PASF.ASSIGNMENT_ID(+)
AND PASF.MANAGER_TYPE (+) = 'LINE_MANAGER'
AND PNI.PERSON_ID(+) = PAAM.PERSON_ID
AND PAAM.PERSON_ID = PPLF.PERSON_ID(+)
and PJFV.JOB_FAMILY_ID= PJFFVL.JOB_FAMILY_ID(+)
AND PJFV.JOB_ID(+) = PAAM.JOB_ID
AND HOIF.ORGANIZATION_ID(+)= PAAM.ORGANIZATION_ID
AND HOIF.ORG_INFORMATION_CONTEXT(+) = 'DEPT_DETAILS'
AND PPAUF.PERSON_ID(+)= PAPF.PERSON_ID
AND PPAUF.ADDRESS_TYPE (+) = 'HOME'
AND PPAUF.ADDRESS_ID= PAF.ADDRESS_ID(+)
AND PGFTL.GRADE_ID(+)= PAAM.GRADE_ID
AND PJF.JOB_ID(+)= PAAM.JOB_ID
AND PJLG.INFORMATION_CATEGORY='HRX_US_JOBS'
and HAOUF.ORGANIZATION_ID(+)=PLE.ORGANIZATION_ID
and PAAM.LEGAL_ENTITY_ID=PLE.ORGANIZATION_ID
AND TRUNC(SYSDATE) BETWEEN PAAM.EFFECTIVE_START_DATE(+) AND PAAM.EFFECTIVE_END_DATE (+)
AND PAAM.PERIOD_OF_SERVICE_ID = PPOS.PERIOD_OF_SERVICE_ID
AND PAPF.PERSON_ID = PAAM.PERSON_ID
AND PP.PERSON_ID = PAAM.PERSON_ID
AND PPNFV.PERSON_ID = PAAM.PERSON_ID
AND HLA.LOCATION_ID(+) = PAAM.LOCATION_ID
AND PJLG.JOB_ID(+) = PAAM.JOB_ID
AND HAOUFVL.ORGANIZATION_ID(+) = PAAM.ORGANIZATION_ID
AND PPNFV.NAME_TYPE = 'GLOBAL'
AND TRUNC(SYSDATE) BETWEEN PPNFV.EFFECTIVE_START_DATE AND PPNFV.EFFECTIVE_END_DATE
AND TRUNC(SYSDATE) BETWEEN PAPF.EFFECTIVE_START_DATE AND PAPF.EFFECTIVE_END_DATE
AND TRUNC(SYSDATE) BETWEEN HAOUFVL.EFFECTIVE_START_DATE(+) AND HAOUFVL.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PPLF.EFFECTIVE_START_DATE(+) AND PPLF.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PJFFVL.EFFECTIVE_START_DATE(+) AND PJFFVL.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PJFV.EFFECTIVE_START_DATE(+) AND PJFV.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN HOIF.EFFECTIVE_START_DATE(+) AND HOIF.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PAF.EFFECTIVE_START_DATE(+) AND PAF.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PPAUF.EFFECTIVE_START_DATE(+) AND PPAUF.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PGFTL.EFFECTIVE_START_DATE(+) AND PGFTL.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PJF.EFFECTIVE_START_DATE(+) AND PJF.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PLE.EFFECTIVE_START_DATE AND PLE.EFFECTIVE_END_DATE
AND TRUNC(SYSDATE) BETWEEN HAOUF.EFFECTIVE_START_DATE(+) AND HAOUF.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN HLA.EFFECTIVE_START_DATE(+) AND HLA.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PJLG.EFFECTIVE_START_DATE(+) AND PJLG.EFFECTIVE_END_DATE(+)
AND TRUNC(SYSDATE) BETWEEN PASF.EFFECTIVE_START_DATE(+) AND PASF.EFFECTIVE_END_DATE(+)
ORDER BY PAPF.PERSON_NUMBER
Run the query in a SQL client that will generate a more detailed error message. For example, the below code is run in SQL*Plus, which correctly indicates that the problem is with the subquery on line 3.
SQL> select
2 (select 1 from dual) this_will_work,
3 (select 1 from dba_objects) this_will_fail,
4 (select 1 from dual) this_will_work
5 from dual;
(select 1 from dba_objects) this_will_fail,
*
ERROR at line 3:
ORA-01427: single-row subquery returns more than one row
The code below is to get distinct data in terms of column name e1 and mdl, but does not show any reslut.
I have put "AND ROWNUM IN (SELECT MAX(ROWNUM) FROM T1.." to remove dulicated row.
If I remove "AND ROWNUM IN (SELECT MAX(ROWNUM) FROM T1..", then of course all the data in Table T1 selected.
<Table and data>
CREATE TABLE T1 (
dte VARCHAR2(15),
gu1 VARCHAR2(15),
gu2 VARCHAR2(15),
eq VARCHAR2(15),
mdl VARCHAR2(15),
val VARCHAR2(15)
);
INSERT INTO T1 VALUES('20190801','30','30','E1','M1','1.5');
INSERT INTO T1 VALUES('20190801','30','30','E1','M1',NULL);
INSERT INTO T1 VALUES('20190801','30','30','E1','M1','0');
INSERT INTO T1 VALUES('20190802','30','30','E1','M1','1.5');
INSERT INTO T1 VALUES('20190803','30','30','E3','M1','3.0');
<Code>
SELECT gu1,gu2,eq,mdl
FROM T1
WHERE val <> '0' AND val IS NOT NULL
AND dte >= '20190801' AND dte <= '20190803'
AND gu1 = '30'
AND ROWNUM IN (SELECT MAX(ROWNUM) FROM T1 --to get only one among dulplicated rows in terms of column e1, mdl,
WHERE val <> '0' AND val IS NOT NULL
AND dte >= '20190801' AND dte <= '20190803'
AND gu1 = '30'
GROUP BY eq,mdl)
;
<Expexted result is>
GU1 GU2 EQ MDL
---- ---- ---- ----
30 30 E1 M1
30 30 E3 M1
rownum is generated after the row is output. what you can do instead is to use
row_number analytical function as follows
SELECT * FROM (
SELECT gu1,gu2,eq,mdl,row_number() over(partition by eq,mdl order by dte desc) as rnk
FROM T1
WHERE val <> '0' AND val IS NOT NULL
AND dte >= '20190801' AND dte <= '20190803'
AND gu1 = '30'
)x
WHERE x.rnk=1
Try to use SELECT DISTINCT Statement.
SELECT DISTINCT
gu1,gu2,eq,mdl
FROM T1
WHERE val <> '0' AND val IS NOT NULL
AND dte >= '20190801' AND dte <= '20190803'
AND gu1 = '30'
;
More info on DISTINCT use here
As far as I understood from sample data and expected output, You can use one of the following method:
Distinct - as decribed in VSMent answer
Using rownum - as described in George Joseph answer
Using EXISTS as described following
-- in following example, you can also use WITH AS to remove excess duplicate coding
SELECT T1.gu1,T1.gu2,T1.eq,T1.mdl
FROM T1
WHERE T1 val <> '0'
AND T1.val IS NOT NULL
AND T1.dte >= '20190801'
AND T1.dte <= '20190803'
AND T1.gu1 = '30'
AND NOT EXISTS (SELECT 1
FROM T2
WHERE T2.val <> '0'
AND T2.val IS NOT NULL
AND T2.dte >= '20190801'
AND T2.dte <= '20190803'
AND T2.GU1 = 30
-- FOLLOWING 3 CONDITION WILL RESTRICT DUPLICATE ROWS
AND T1.EQ = T2.EQ
AND T1.MDL = T2.MDL
T1.ROWID > T2.ROWID
);
Cheers!!
I have some code that previously ran fine with no issues. But now when I run I receive the
ORA-02395: exceeded call limit on IO usage
02395. 00000 - "exceeded call limit on IO usage"
error. Can anyone explain to me why my code is throwing this error. I have no access to increase any user privileges. I've tried running this code and limiting the amount of data in the with clause and it ran fine. So I'm guessing some modifications need to happen there.
WITH NEW_REP_DATA AS (
select period,manager rep,comp,sum(a) "GT99",sum(b) "90TO99",sum(c) "80TO89",sum(d) "70TO79",sum(e) "LT70",sum(f) "NA" from (
select period
,rep_code
,manager
--,nvl(a,0)+nvl(b,0)+nvl(c,0)+nvl(d,0)+nvl(e,0)+nvl(f,0) cnt
,comp,
nvl(a,0) a,nvl(b,0) b, nvl(c,0) c, nvl(d,0) d,nvl(e,0) e,nvl(f,0) f
from
(select period,rep_code,manager, comp,max(case when bucket='>99' then cnt end) a,
max(case when bucket='90TO99' then cnt end) b,
max(case when bucket='80TO89' then cnt end) c,
max(case when bucket= '70TO79' then cnt end) d,
max(case when bucket='LT70' then cnt end) e,
max(case when bucket='NA' then cnt end) f
from (
select period,rep_code,manager, comp,bucket,
--count(unique rep_code)
count( distinct rep_code) cnt
--cnt
from(
select * from (select
unique period
,MANAGER
,"PayeeID" rep_code
,comp
,cytd cytd_a
,cytp cytd_p,
nvl(case when cytp > 0 then
case when round((cytd/cytp),3) > .99 then '>99'
when round((cytd/cytp),3) between .891 and .99 then '90TO99'
when round((cytd/cytp),3) between .791 and .89 then '80TO89'
when round((cytd/cytp),3) between .7 and .79 then '70TO79'
when round((cytd/cytp),3) < .7 then 'LT70'
end
when cytp = 0 and cytd > 0 then '>99'
else 'NA'
end,'NA') as bucket
from (
select aaa.period
,aaa."PayeeID"
,aaa."Reports_to" MANAGER
,aaa."Component" comp,
aaa."Current_YTD_Actual" cytd,
aaa."Current_YTD_Plan" cytp
from nbr_var_data aaa
where aaa."Comp_Plan_Name" not in ('MISC_COMP','GM_2017')
AND "Comp_Plan_Name" not in ('MISC_COMP')
AND "Comp_Plan_Name" not LIKE '%GM%'
and aaa.period = (select max(aaa.period) from Nbr_Var_Data)
))
)
where 1=1
group by period, rep_code, comp, bucket, manager )
group by period, rep_code, comp, manager )) group by period, manager, comp)
SELECT DISTINCT
dc.rep PID
,dc.period
,ee.PAYEE_NAM
,dc.comp
,Dc."GT99"
,Dc."90TO99"
,Dc."80TO89"
,Dc."70TO79"
,Dc."LT70"
,Dc."NA"
,Ee.Parent_Payee_Id REPORT_TO_PAYEE_ID
,Ee.Parent_Payee_Nam REPORT_TO_NAME
FROM (SELECT
--period ,
--empl_id ,
gg.payee_id ,
gg.payee_nam ,
--lvl ,
ff.parent_payee_id ,
ff.PARENT_PAYEE_NAM
--parent_lvl ,
--mnth_disp
FROM (SELECT DISTINCT
dd.period
,dd.PARENT_PAYEE_ID
,ee.PAYEE_NAM PARENT_PAYEE_NAM
from (
SELECT DISTINCT
PERIOD,
PARENT_PAYEE_ID
FROM FI_CHANNEL_HIER) dd
left join FI_CHANNEL_HIER ee
on dd.PARENT_PAYEE_ID = ee.PAYEE_ID
WHERE DD.PERIOD = (select max(DD.PERIOD) from FI_CHANNEL_HIER )) ff
LEFT JOIN FI_CHANNEL_HIER gg
ON ff.PARENT_PAYEE_ID = gg.PARENT_PAYEE_ID) ee,
NEW_REP_DATA dc
WHERE EE.PAYEE_ID = DC.Rep
;
ORA-02395 happens when your query exceeds the LOGICAL_READS_PER_CALL threshold set in your user profile. That's why the issue goes away when you restricted the amount of data returned by your subquery.
Two possible explanations for why this did not use to happen:
The tables contain more data than they used to be.
Your DBA has implemented a new user profile (or made the existing one stricter).
The obvious solution is to negotiate with the DBA.
Otherwise you will need to refactor your query to reduce the number of blocks it scans. Doing requires an understanding of your data model, your business logic and your data characteristics (volume, distribution, usage).
Clearly that requires knowledge of and access to your system, so it's not something that we can help with.
WITH NEW_REP_DATA AS (
select period,manager rep,comp,sum(a) "GT99",sum(b) "90TO99",sum(c) "80TO89",sum(d) "70TO79",sum(e) "LT70",sum(f) "NA" from (
select period
,rep_code
,manager
--,nvl(a,0)+nvl(b,0)+nvl(c,0)+nvl(d,0)+nvl(e,0)+nvl(f,0) cnt
,comp,
nvl(a,0) a,nvl(b,0) b, nvl(c,0) c, nvl(d,0) d,nvl(e,0) e,nvl(f,0) f
from
(select period,rep_code,manager, comp,max(case when bucket='>99' then cnt end) a,
max(case when bucket='90TO99' then cnt end) b,
max(case when bucket='80TO89' then cnt end) c,
max(case when bucket= '70TO79' then cnt end) d,
max(case when bucket='LT70' then cnt end) e,
max(case when bucket='NA' then cnt end) f
from (
select period,rep_code,manager, comp,bucket,
--count(unique rep_code)
count( distinct rep_code) cnt
--cnt
from(
select * from (select
unique period
,MANAGER
,"PayeeID" rep_code
,comp
,cytd cytd_a
,cytp cytd_p,
nvl(case when cytp > 0 then
case when round((cytd/cytp),3) > .99 then '>99'
when round((cytd/cytp),3) between .891 and .99 then '90TO99'
when round((cytd/cytp),3) between .791 and .89 then '80TO89'
when round((cytd/cytp),3) between .7 and .79 then '70TO79'
when round((cytd/cytp),3) < .7 then 'LT70'
end
when cytp = 0 and cytd > 0 then '>99'
else 'NA'
end,'NA') as bucket
from (
select aaa.period
,aaa."PayeeID"
,aaa."Reports_to" MANAGER
,aaa."Component" comp,
aaa."Current_YTD_Actual" cytd,
aaa."Current_YTD_Plan" cytp
from nbr_var_data aaa
where aaa."Comp_Plan_Name" not in ('MISC_COMP','GM_2017')
AND "Comp_Plan_Name" not in ('MISC_COMP')
AND "Comp_Plan_Name" not LIKE '%GM%'
and aaa.period = (select max(aaa.period) from Nbr_Var_Data)
))
)
where 1=1
group by period, rep_code, comp, bucket, manager )
group by period, rep_code, comp, manager )) group by period, manager, comp)
SELECT DISTINCT
dc.rep PID
,dc.period
,ee.PAYEE_NAM
,dc.comp
,Dc."GT99"
,Dc."90TO99"
,Dc."80TO89"
,Dc."70TO79"
,Dc."LT70"
,Dc."NA"
,Ee.Parent_Payee_Id REPORT_TO_PAYEE_ID
,Ee.Parent_Payee_Nam REPORT_TO_NAME
FROM (SELECT
--period ,
--empl_id ,
gg.payee_id ,
gg.payee_nam ,
--lvl ,
ff.parent_payee_id ,
ff.PARENT_PAYEE_NAM
--parent_lvl ,
--mnth_disp
FROM (SELECT DISTINCT
dd.period
,dd.PARENT_PAYEE_ID
,ee.PAYEE_NAM PARENT_PAYEE_NAM
from (
SELECT DISTINCT
PERIOD,
PARENT_PAYEE_ID
FROM FI_CHANNEL_HIER) dd
left join FI_CHANNEL_HIER ee
on dd.PARENT_PAYEE_ID = ee.PAYEE_ID
WHERE DD.PERIOD = (select max(DD.PERIOD) from FI_CHANNEL_HIER )) ff
LEFT JOIN FI_CHANNEL_HIER gg
ON ff.PARENT_PAYEE_ID = gg.PARENT_PAYEE_ID) ee,
NEW_REP_DATA dc
WHERE EE.PAYEE_ID = DC.Rep
AND dc.period = (select max(period) from NEW_REP_DATA)
;
Please see my code below as it is running too slowly with the CROSS APPLY.
How can I remove the CROSS APPLY and add something else that will run faster?
Please note I am using SQL Server 2008 R2.
;WITH MyCTE AS
(
SELECT
R.NetWinCURRENCYValue AS NetWin
,dD.[Date] AS TheDay
FROM
dimPlayer AS P
JOIN
dbo.factRevenue AS R ON P.playerKey = R.playerKey
JOIN
dbo.vw_Date AS dD ON Dd.dateKey = R.dateKey
WHERE
P.CustomerID = 12345)
SELECT
A.TheDay AS [Date]
,ISNULL(A.NetWin, 0) AS NetWin
,rt.runningTotal AS CumulativeNetWin
FROM MyCTE AS A
CROSS APPLY (SELECT SUM(NetWin) AS runningTotal
FROM MyCTE WHERE TheDay <= A.TheDay) AS rt
ORDER BY A.TheDay
CREATE TABLE #temp (NetWin money, TheDay datetime)
insert into #temp
SELECT
R.NetWinCURRENCYValue AS NetWin
,dD.[Date] AS TheDay
FROM
dimPlayer AS P
JOIN
dbo.factRevenue AS R ON P.playerKey = R.playerKey
JOIN
dbo.vw_Date AS dD ON Dd.dateKey = R.dateKey
WHERE
P.CustomerID = 12345;
SELECT
A.TheDay AS [Date]
,ISNULL(A.NetWin, 0) AS NetWin
,SUM(B.NetWin) AS CumulativeNetWin
FROM #temp AS A
JOIN #temp AS B
ON A.TheDay >= B.TheDay
GROUP BY A.TheDay, ISNULL(A.NetWin, 0);
Here https://stackoverflow.com/a/13744550/613130 it's suggested to use recursive CTE.
;WITH MyCTE AS
(
SELECT
R.NetWinCURRENCYValue AS NetWin
,dD.[Date] AS TheDay
,ROW_NUMBER() OVER (ORDER BY dD.[Date]) AS RN
FROM dimPlayer AS P
JOIN dbo.factRevenue AS R ON P.playerKey = R.playerKey
JOIN dbo.vw_Date AS dD ON Dd.dateKey = R.dateKey
WHERE P.CustomerID = 12345
)
, MyCTERec AS
(
SELECT C.TheDay AS [Date]
,ISNULL(C.NetWin, 0) AS NetWin
,ISNULL(C.NetWin, 0) AS CumulativeNetWin
,C.RN
FROM MyCTE AS C
WHERE C.RN = 1
UNION ALL
SELECT C.TheDay AS [Date]
,ISNULL(C.NetWin, 0) AS NetWin
,P.CumulativeNetWin + ISNULL(C.NetWin, 0) AS CumulativeNetWin
,C.RN
FROM MyCTERec P
INNER JOIN MyCTE AS C ON C.RN = P.RN + 1
)
SELECT *
FROM MyCTERec
ORDER BY RN
OPTION (MAXRECURSION 0)
Note that this query will work if you have 1 record == 1 day! If you have multiple records in a day, the results will be different from the other query.
As I said here, if you want really fast calculation, put it into temporary table with sequential primary key and then calculate rolling total:
create table #Temp (
ID bigint identity(1, 1) primary key,
[Date] date,
NetWin decimal(29, 10)
)
insert into #Temp ([Date], NetWin)
select
dD.[Date],
sum(R.NetWinCURRENCYValue) as NetWin,
from dbo.dimPlayer as P
inner join dbo.factRevenue as R on P.playerKey = R.playerKey
inner join dbo.vw_Date as dD on Dd.dateKey = R.dateKey
where P.CustomerID = 12345
group by dD.[Date]
order by dD.[Date]
;with cte as (
select T.ID, T.[Date], T.NetWin, T.NetWin as CumulativeNetWin
from #Temp as T
where T.ID = 1
union all
select T.ID, T.[Date], T.NetWin, T.NetWin + C.CumulativeNetWin as CumulativeNetWin
from cte as C
inner join #Temp as T on T.ID = C.ID + 1
)
select C.[Date], C.NetWin, C.CumulativeNetWin
from cte as C
order by C.[Date]
I assume that you could have duplicates dates in the input, but don't want duplicates in the output, so I grouped data before puting it into the table.