SQL Query with Left Join slows down - sql

Cleaning up some mess in a query that was written couple of years ago and I noticed that the below query is the cause of slowing down the result. I tried to search for alternatives but was not too successful.
WITH cat
AS (SELECT main.category_id main_id,
main.description main_desc,
sub1.category_id sub1_id,
sub1.description sub1_desc,
sub2.category_id sub2_id,
sub2.description sub2_desc,
sub3.category_id sub3_id,
sub3.description sub3_desc,
NVL (
NVL (NVL (sub3.category_id, sub2.category_id),
sub1.category_id),
main.category_id)
lowest
FROM iod.sr_category main
-- We need to insert a temporary null row and include it in the join so the categories which are not leaves are also included
LEFT JOIN
(SELECT category_id, description, parent_id
FROM iod.sr_category
UNION
SELECT NULL, NULL, NULL
FROM DUAL) sub1
ON ( main.category_id = sub1.parent_id
OR sub1.category_id IS NULL)
LEFT JOIN
(SELECT category_id, description, parent_id
FROM iod.sr_category
UNION
SELECT NULL, NULL, NULL
FROM DUAL) sub2
ON ( sub1.category_id = sub2.parent_id
OR sub2.category_id IS NULL)
LEFT JOIN
(SELECT category_id, description, parent_id
FROM iod.sr_category
UNION
SELECT NULL, NULL, NULL
FROM DUAL) sub3
ON ( sub2.category_id = sub3.parent_id
OR sub3.category_id IS NULL)
WHERE main.parent_id IS NULL
AND (main.category_id IN ( :main_category))
AND ( NVL ( :sub_category1, 0) = 0
OR sub1.category_id = :sub_category1)
AND ( NVL ( :sub_category2, 0) = 0
OR sub2.category_id = :sub_category2)
AND ( NVL ( :sub_category3, 0) = 0
OR sub3.category_id = :sub_category3))
SELECT r.sector_report_id,
r.created_date report_created_date --, nvl(f.sch_dep_date_local_port, f.sch_dep_date_gmt) report_search_date
,
c.main_desc,
c.sub1_desc,
c.sub2_desc,
c.sub3_desc,
p.problem_type_code,
p.description,
e.first_name,
e.last_name,
e.employee_id,
cl.description class,
ROW_NUMBER ()
OVER (PARTITION BY r.sector_report_id, cl.class_id
ORDER BY q.question_id)
question_num,
q.label question,
q.data_type,
NVL (
pa.answer,
NVL (
a.VALUE,
NVL (a.free_text,
NVL (a.hours || ':' || a.minutes, 'NO ANSWER'))))
answer,
f.arln_code,
f.flight_number,
f.sch_dep_date_gmt,
f.sch_dep_date_local_port,
f.sch_dep_apt,
f.sch_arv_apt,
f.tail_number,
f.fleet_code,
LISTAGG (
e_cmt.first_name
|| ' '
|| e_cmt.last_name
|| ' '
|| cmt.created_date
|| ':'
|| cmt.comment_text,
' ' || CHR (10))
WITHIN GROUP (ORDER BY cmt.created_date)
comments,
(NVL ( (SELECT COUNT (*)
FROM iod.sr_photograph p
WHERE p.sector_report_id = r.sector_report_id),
0))
AS photo
FROM iod.sr_sector_report r
JOIN cat c ON c.lowest = r.category_id
LEFT JOIN iod.sr_problem_type p
ON p.problem_type_id = r.problem_type_id
LEFT JOIN iod.ops_flight_leg f
ON f.ops_flight_leg_id = r.ops_flight_leg_id
LEFT JOIN iod.employee e ON e.employee_id = r.employee_id
LEFT JOIN iod.sr_class_sector_report csr
ON csr.sector_report_id = r.sector_report_id
LEFT JOIN iod.sr_class cl ON cl.class_id = csr.class_id
LEFT JOIN iod.sr_answer a ON a.sector_report_id = r.sector_report_id
LEFT JOIN iod.sr_predefined_answer pa
ON pa.predefined_answer_id = a.predefined_answer_id
LEFT JOIN iod.sr_question q ON q.question_id = a.question_id
LEFT JOIN iod.sr_comment cmt
ON cmt.sector_report_id = r.sector_report_id
LEFT JOIN iod.employee e_cmt ON e_cmt.employee_id = cmt.employee_id
WHERE (NVL ( :problem_type, 0) = 0 OR p.problem_type_id = :problem_type)
AND TRUNC (f.sch_dep_date_local_port) BETWEEN :from_date AND :TO_DATE
--and cast(nvl(f.sch_dep_date_local_port, f.sch_dep_date_gmt) as date) between :from_date and :to_date
AND (--nvl(:station_from, ' ') = ' ' or
f.sch_dep_apt IN ( :station_from))
AND (--nvl(:station_to, ' ') = ' ' or
f.sch_arv_apt IN ( :station_to))
GROUP BY r.sector_report_id,
r.created_date,
c.main_desc,
c.sub1_desc,
c.sub2_desc,
c.sub3_desc,
p.problem_type_code,
p.description,
e.first_name,
e.last_name,
e.employee_id,
cl.description,
cl.class_id,
q.label,
q.data_type,
q.question_id,
NVL (
pa.answer,
NVL (
a.VALUE,
NVL (a.free_text,
NVL (a.hours || ':' || a.minutes, 'NO ANSWER')))),
f.arln_code,
f.flight_number,
f.sch_dep_date_gmt,
f.sch_dep_date_local_port,
f.sch_dep_apt,
f.sch_arv_apt,
f.tail_number,
f.fleet_code
ORDER BY TRUNC (f.sch_dep_date_local_port) ASC
so the part
subx.category_id = suby.parent_id OR suby.category_id is null
slows down the query. Any ideas?

Without an explain plan or an understanding of the business logic being implemented all we can do is guess. But this does seem like a scenario where sub-query factoring could help:
with sub as (
SELECT category_id, description, parent_id FROM sr_category
UNION
SELECT null, null, null FROM DUAL)
select
....
from
....
LEFT JOIN sub sub1
ON (main.category_id = sub1.parent_id OR sub1.category_id is null)
LEFT JOIN sub sub2
ON (sub1.category_id = sub2.parent_id OR sub2.category_id is null)
LEFT JOIN sub sub3
ON (sub2.category_id = sub3.parent_id OR sub3.category_id is null)
WHERE main.parent_id is null
AND (main.category_id IN (:main_category))
AND (nvl(:sub_category1, 0) = 0 OR sub1.category_id = :sub_category1)
AND (nvl(:sub_category2, 0) = 0 OR sub2.category_id = :sub_category2)
AND (nvl(:sub_category3, 0) = 0 OR sub3.category_id = :sub_category3))
The WITH clause is covered in the Oracle SQL documentation. Find out more

Related

Inner join on multiple subqueries?

I am writing a select that contains a few subqueries (only one in snippet of code below), however I am having a hard time only returning rows where there is 1 existing row in the AIRFI_TCD subquery. I only want rows to be displayed when there is a matching CLT_ID on TSUMM and TPPPRFL tables. I tried the HAVING COUNT clause (see below), but that didn't work. I am blanking on how to join TSUMM with my subquery.
SELECT RIGHT(DIGITS (A.CLT_ID),9) || A.PGM_ID ||
RIGHT(DIGITS (A.PGM_ACCT_SQNBR),4) AS BN
, ( SELECT B.XREF_NBR
FROM GHMTUO#1.TPPPRFL B
WHERE B.PARTIC_PRFL_TCD = '04' --AIRFI
AND B.CLT_ID = A.CLT_ID
AND B.BUS_PGM_ID = A.PGM_ID
AND B.CLT_ID_TCD = '01'
AND B.PARTIC_PRFL_EDT = (SELECT MAX(X.PARTIC_PRFL_EDT)
FROM GHMTUO#1.TPPPRFL X
WHERE X.CLT_ID = B.CLT_ID)
AND B.PARTIC_PRFL_CDTTM = (SELECT MAX(Z.PARTIC_PRFL_CDTTM)
FROM GHMTUO#1.TPPPRFL Z
WHERE Z.CLT_ID = B.CLT_ID)
) AS AIRFI_TCD
FROM RAMTUO#1.TSUMM A
WHERE A.PGM_ID = 'RT'
GROUP BY A.CLT_ID, A.PGM_ID, A.PGM_ACCT_SQNBR
HAVING COUNT(AIRFI_TCD) > 1
WITH UR;
Thank you!
I this an INNER JOIN solution?
SELECT RIGHT(DIGITS (A.CLT_ID),9) || A.PGM_ID ||
RIGHT(DIGITS (A.PGM_ACCT_SQNBR),4) AS BN,
B.XREF_NBR AS AIRFI_TCD
FROM RAMTUO#1.TSUMM A INNER JOIN GHMTUO#1.TPPPRFL B ON B.CLT_ID = A.CLT_ID AND B.BUS_PGM_ID = A.PGM_ID
WHERE A.PGM_ID = 'RT' AND B.PARTIC_PRFL_TCD = '04'
AND ...
GROUP BY A.CLT_ID, A.PGM_ID, A.PGM_ACCT_SQNBR
rows will only appear if there is a matching CLT_ID and a matching PGM_ID
If you need just existence, then try this:
SELECT RIGHT(DIGITS (A.CLT_ID),9) || A.PGM_ID ||
RIGHT(DIGITS (A.PGM_ACCT_SQNBR),4) AS BN
FROM RAMTUO#1.TSUMM A
WHERE A.PGM_ID = 'RT'
AND EXISTS
(
SELECT 1
FROM GHMTUO#1.TPPPRFL B
JOIN
(
SELECT CL
T_ID
, MAX(PARTIC_PRFL_EDT) PARTIC_PRFL_EDT
, MAX(PARTIC_PRFL_CDTTM) PARTIC_PRFL_CDTTM
FROM GHMTUO#1.TPPPRFL
GROUP BY CLT_ID
) X ON X.CLT_ID = B.CLT_ID
AND X.PARTIC_PRFL_EDT = B.PARTIC_PRFL_EDT
AND X.PARTIC_PRFL_CDTTM = B.PARTIC_PRFL_CDTTM
WHERE B.PARTIC_PRFL_TCD = '04' --AIRFI
AND B.CLT_ID = A.CLT_ID
AND B.BUS_PGM_ID = A.PGM_ID
AND B.CLT_ID_TCD = '01'
)
;
One thing I particularly like about DB2 is the ability to move complicated expressions or even subqueries into new virtual data fields thanks to JOIN TABLE (...), something like this:
SELECT RIGHT(DIGITS (A.CLT_ID),9) || A.PGM_ID ||
RIGHT(DIGITS (A.PGM_ACCT_SQNBR),4) AS BN
, subq.AIRFI_TCD
FROM RAMTUO#1.TSUMM A
INNER JOIN TABLE
( SELECT B.XREF_NBR,
COUNT(*)OVER() AS total_amount --number of matching entries
FROM GHMTUO#1.TPPPRFL B
WHERE B.PARTIC_PRFL_TCD = '04' --AIRFI
AND B.CLT_ID = A.CLT_ID
AND B.BUS_PGM_ID = A.PGM_ID
AND B.CLT_ID_TCD = '01'
AND B.PARTIC_PRFL_EDT = (SELECT MAX(X.PARTIC_PRFL_EDT)
FROM GHMTUO#1.TPPPRFL X
WHERE X.CLT_ID = B.CLT_ID)
AND B.PARTIC_PRFL_CDTTM = (SELECT MAX(Z.PARTIC_PRFL_CDTTM)
FROM GHMTUO#1.TPPPRFL Z
WHERE Z.CLT_ID = B.CLT_ID)
) AS subq (AIRFI_TCD, total_amount) ON subq.total_amount = 1 --instead of HAVING
WHERE A.PGM_ID = 'RT'
WITH UR;
Try it, I hope it works.

Why is Oracle REPLACE function not working for this string?

We have a pattern we use all the time and this is usually pretty straightforward.
sortOrder IN VARCHAR2 := 'Title'
query VARCHAR2(32767) := q'[
SELECT
Columns
FROM tables
ORDER BY {sortOrder}
]';
query := REPLACE(query, '{sortOrder}', sortOrder);
But for this string it is not working:
WITH appl_List
AS
(
SELECT DISTINCT
appls.admin_phs_ORG_code || TO_CHAR(appls.serial_num, 'FM000000') AS core_proj_number,
appls.Appl_ID
FROM TABLE(:portfolioTable) appls
),
g1SupportingProjCount AS
(
SELECT
gen1grants.silverchair_id,
COUNT(DISTINCT al.Appl_ID) AS ApplCount
FROM
appl_List al
JOIN cg_cited_reference_gen1_grant gen1grants
ON al.core_proj_number = gen1grants.ic_serial_num
JOIN cg_cited_reference_gen1 gen1refs
ON gen1grants.silverchair_id = gen1refs.silverchair_id
GROUP BY gen1grants.Silverchair_id
),
g1SupportedPubCount AS
(
SELECT
gen1grants.silverchair_id,
COUNT(DISTINCT gen1refs.gen1_wos_uid) AS PubCount
FROM
appl_List al
JOIN cg_cited_reference_gen1_grant gen1grants
ON al.core_proj_number = gen1grants.ic_serial_num
JOIN cg_cited_reference_gen1 gen1refs
ON gen1grants.silverchair_id = gen1refs.silverchair_id
GROUP BY gen1grants.Silverchair_id
),
g2SupportingProjCount AS
(
SELECT
gen2grants.silverchair_id,
COUNT(DISTINCT al.Appl_ID) AS ApplCount
FROM
appl_List al
JOIN cg_cited_reference_gen2_grant gen2grants
ON al.core_proj_number = gen2grants.ic_serial_num
JOIN cg_cited_reference_gen2 gen2refs
ON gen2grants.silverchair_id = gen2refs.silverchair_id
GROUP BY gen2grants.Silverchair_id
),
g2SupportedPubCount AS
(
SELECT
gen2grants.silverchair_id,
COUNT(DISTINCT gen2refs.gen2_wos_uid) AS PubCount
FROM
appl_List al
JOIN cg_cited_reference_gen2_grant gen2grants
ON al.core_proj_number = gen2grants.ic_serial_num
JOIN cg_cited_reference_gen2 gen2refs
ON gen2grants.silverchair_id = gen2refs.silverchair_id
GROUP BY gen2grants.Silverchair_id
),
portfolio_cg_ids AS
(
SELECT DISTINCT md.silverchair_id
FROM
(
SELECT silverchair_id
FROM cg_cited_reference_gen1_grant gen1Grants
JOIN Appl_List appls
ON appls.core_proj_number = gen1Grants.ic_serial_num
UNION
SELECT silverchair_id
FROM cg_cited_reference_gen2_grant gen2Grants
JOIN Appl_List appls
ON appls.core_proj_number = gen2Grants.ic_serial_num
) grant_sc_ids
JOIN cg_metadata md
ON grant_sc_ids.silverchair_id = md.silverchair_id
)
SELECT
silverchairId,
TITLE,
PMID,
PMCID,
publication_year as year,
referenceCount1Gen,
supportingProjectCount1Gen,
supportedPublicationCount1Gen,
referenceCount2Gen,
supportingProjectCount2Gen,
supportedPublicationCount2Gen,
COUNT(1) OVER() as TotalCount
FROM
(
SELECT
md.SILVERCHAIR_ID silverchairId,
md.TITLE,
md.PMID,
md.PMCID ,
md.publication_year as year,
g1RefCounts.referenceCount1Gen as referenceCount1Gen,
g1SupportingProjCount.ApplCount as supportingProjectCount1Gen,
g1SupportedPubCount.PubCount as supportedPublicationCount1Gen,
g2RefCounts.referenceCount2Gen as referenceCount2Gen,
g2SupportingProjCount.ApplCount as supportingProjectCount2Gen,
g2SupportedPubCount.PubCount as supportedPublicationCount2Gen,
--COUNT(1) OVER() as TotalCount
FROM cg_metadata md
-- BEGIN datascope to current portfolio
JOIN portfolio_cg_ids
ON portfolio_cg_ids.silverchair_id = md.silverchair_id
-- END datascope to current portfolio
LEFT JOIN g1SupportingProjCount
ON g1SupportingProjCount.Silverchair_id = md.silverchair_id
LEFT JOIN g2SupportingProjCount
ON g2SupportingProjCount.Silverchair_id = md.silverchair_id
LEFT JOIN g1SupportedPubCount
ON g1SupportedPubCount.Silverchair_id = md.silverchair_id
LEFT JOIN g2SupportedPubCount
ON g2SupportedPubCount.Silverchair_id = md.silverchair_id
OUTER APPLY
(
Select Count(*) as referenceCount1Gen
FROM cg_cited_reference_gen1 g1Refs
WHERE g1Refs.silverchair_id = md.silverchair_id
) g1RefCounts
OUTER APPLY
(
Select Count(*) as referenceCount2Gen
FROM cg_cited_reference_gen2 g2Refs
WHERE g2Refs.silverchair_id = md.silverchair_id
) g2RefCounts
) results
ORDER BY {sortOrder}
Are there cases where some kind of special char in the string can keep this from working?
I'm kind of perplexed. I've been using this pattern for like 3 years and I've never had this not work.
What could be breaking this?
The query has 4000+ characters.
The text is probably being truncated somewhere down the line.

SQL Server - get rid of derived query in join

I'm interested in some suggestions with examples on how to 1)get rid of the derived query in the left outer join and 2)remove/consolidate the cte cteSecType in the code below The derived query in the join chooses the minimum assistant_personnel_number out of a list; there can be multiple primary and backup assistants associated with an employee . thx
WITH cteAssistants ( executive_personnel_number, assistant_personnel_number, assistant_type, assign_role )
AS ( SELECT CASE WHEN ISNUMERIC(JP.XA_asgn_emplid) = 1
THEN JP.XA_asgn_emplid
ELSE ''
END AS executive_personnel_number ,
JAP.emplid AS assistant_personnel_number ,
LAT1.FIELDVALUE AS assistant_type ,
LAT3.xlatshortname AS assign_role
FROM dbo.XA_ASGN_PRNT AS KAP
LEFT OUTER JOIN dbo.XA_ASSIGNMENTS AS KA ON JP.emplid = JAP.emplid
AND JP.effdt = JAP.effdt
LEFT OUTER JOIN dbo.XA_EMPLOYEES AS EXECT ON EXECT.EMPLID = JP.XA_ASGN_EMPLID
LEFT OUTER JOIN dbo.XA_EMPLOYEES AS ASST ON ASST.EMPLID = JAP.EMPLID
LEFT OUTER JOIN dbo.XLATITEM AS XLAT1 ON LAT1.fieldname = 'XA_ASGN_TYPE'
AND LAT1.fieldvalue = JAP.XA_asgn_type
LEFT OUTER JOIN dbo.XLATITEM AS XLAT3 ON LAT3.fieldname = 'XA_ASGN_ROLE'
AND LAT3.fieldvalue = JP.XA_asgn_role
WHERE JAP.effdt = ( SELECT MAX(effdt)
FROM dbo.XA_ASGN_PRNT
WHERE emplid = JAP.emplid
AND effdt <= GETDATE()
)
--Return data only when both executive and assistant are still active; null is for Floaters
AND ( EXECT.HR_STATUS = 'A'
OR EXECT.HR_STATUS IS NULL
)
AND ASST.HR_STATUS = 'A'
AND ( JAP.XA_asgn_type = 'F'
OR ( JAP.XA_asgn_type IN ( 'A', 'AF' )
AND JP.XA_asgn_person = 'Y'
)
)
),
cteSecType ( assistant_personnel_number, SecType )
AS ( SELECT assistant_personnel_number ,
assistant_type AS SecType
FROM cteAssistants
GROUP BY assistant_type ,
assistant_personnel_number
)
SELECT EMP.XA_NETWORK_ID AS network_id ,
EMP.XA_EMPLID AS empid ,
EMP.XA_EMPLID AS employeeNumber ,
EMP.XA_FIRST_NAME AS first_name ,
EMP.XA_LAST_NAME AS last_name ,
SECRES.SecType AS SecType ,
AsstInfo.XA_fml_pref_name AS PrimaryAssistant_FML ,
AsstInfo.XA_network_id AS PrimaryAssistant_Newtork_ID ,
AsstInfo.XA_EMPLID AS PrimaryAssistant_EmpID ,
AsstInfo.XA_phone_ext AS PrimaryAssitant_Extension
FROM dbo.XA_EMPLOYEES AS EMP
LEFT OUTER JOIN dbo.XA_EMPLOYEES AS MGR ON EMP.supervisor_id = MGR.emplid
LEFT OUTER JOIN cteSecType AS SECRES ON CAST(CAST(SECRES.assistant_personnel_number AS INT) AS VARCHAR(11)) = EMP.XA_EMPLID
LEFT OUTER JOIN ( SELECT executive_personnel_number AS AttorneyID ,
MIN(assistant_personnel_number) AS AssistantID
FROM cteEmployeeAssistants AS A
WHERE ( assign_role = 'Primary' )
GROUP BY executive_personnel_number
) AS ASST ON ASST.AttorneyID = EMP.XA_EMPLID
LEFT OUTER JOIN dbo.XA_EMPLOYEES AS ASSTINFO ON ASSTINFO.XA_EMPLID = ASST.AssistantID
WHERE ( EMP.HR_STATUS = 'A' )
AND ( EMP.PER_ORG IN ( 'EMP', 'CWR' ) )
AND ( ISNULL(EMP.XA_NETWORK_ID, '') <> '' )
GO

How to fix Ora-01427 single-row subquery returns more than one row in select?

When i execute the following query, i get the message like
"Ora-01427 single-row subquery returns more than one row"
SELECT E.I_EmpID AS EMPID,
E.I_EMPCODE AS EMPCODE,
E.I_EmpName AS EMPNAME,
REPLACE(TO_CHAR(A.I_REQDATE, 'DD-Mon-YYYY'), ' ', '') AS FROMDATE,
REPLACE(TO_CHAR(A.I_ENDDATE, 'DD-Mon-YYYY'), ' ', '') AS TODATE,
TO_CHAR(NOD) AS NOD,
DECODE(A.I_DURATION,
'FD',
'FullDay',
'FN',
'ForeNoon',
'AN',
'AfterNoon') AS DURATION,
L.I_LeaveType AS LEAVETYPE,
REPLACE(TO_CHAR((SELECT C.I_WORKDATE
FROM T_COMPENSATION C
WHERE C.I_COMPENSATEDDATE = A.I_REQDATE
AND C.I_EMPID = A.I_EMPID),
'DD-Mon-YYYY'),
' ',
'') AS WORKDATE,
A.I_REASON AS REASON,
AP.I_REJECTREASON AS REJECTREASON
FROM T_LEAVEAPPLY A
INNER JOIN T_EMPLOYEE_MS E
ON A.I_EMPID = E.I_EmpID
AND UPPER(E.I_IsActive) = 'YES'
AND A.I_STATUS = '1'
INNER JOIN T_LeaveType_MS L
ON A.I_LEAVETYPEID = L.I_LEAVETYPEID
LEFT OUTER JOIN T_APPROVAL AP
ON A.I_REQDATE = AP.I_REQDATE
AND A.I_EMPID = AP.I_EMPID
AND AP.I_APPROVALSTATUS = '1'
WHERE E.I_EMPID <> '22'
ORDER BY A.I_REQDATE DESC
when i execute this without ORDER BY A.I_REQDATE DESC it returns 100 rows...
Use the following query:
SELECT E.I_EmpID AS EMPID,
E.I_EMPCODE AS EMPCODE,
E.I_EmpName AS EMPNAME,
REPLACE(TO_CHAR(A.I_REQDATE, 'DD-Mon-YYYY'), ' ', '') AS FROMDATE,
REPLACE(TO_CHAR(A.I_ENDDATE, 'DD-Mon-YYYY'), ' ', '') AS TODATE,
TO_CHAR(NOD) AS NOD,
DECODE(A.I_DURATION,
'FD',
'FullDay',
'FN',
'ForeNoon',
'AN',
'AfterNoon') AS DURATION,
L.I_LeaveType AS LEAVETYPE,
REPLACE(TO_CHAR((SELECT max(C.I_WORKDATE)
FROM T_COMPENSATION C
WHERE C.I_COMPENSATEDDATE = A.I_REQDATE
AND C.I_EMPID = A.I_EMPID),
'DD-Mon-YYYY'),
' ',
'') AS WORKDATE,
A.I_REASON AS REASON,
AP.I_REJECTREASON AS REJECTREASON
FROM T_LEAVEAPPLY A
INNER JOIN T_EMPLOYEE_MS E
ON A.I_EMPID = E.I_EmpID
AND UPPER(E.I_IsActive) = 'YES'
AND A.I_STATUS = '1'
INNER JOIN T_LeaveType_MS L
ON A.I_LEAVETYPEID = L.I_LEAVETYPEID
LEFT OUTER JOIN T_APPROVAL AP
ON A.I_REQDATE = AP.I_REQDATE
AND A.I_EMPID = AP.I_EMPID
AND AP.I_APPROVALSTATUS = '1'
WHERE E.I_EMPID <> '22'
ORDER BY A.I_REQDATE DESC
The trick is to force the inner query return only one record by adding an aggregate function (I have used max() here). This will work perfectly as far as the query is concerned, but, honestly, OP should investigate why the inner query is returning multiple records by examining the data. Are these multiple records really relevant business wise?
The only subquery appears to be this - try adding a ROWNUM limit to the where to be sure:
(SELECT C.I_WORKDATE
FROM T_COMPENSATION C
WHERE C.I_COMPENSATEDDATE = A.I_REQDATE AND ROWNUM <= 1
AND C.I_EMPID = A.I_EMPID)
You do need to investigate why this isn't unique, however - e.g. the employee might have had more than one C.I_COMPENSATEDDATE on the matched date.
For performance reasons, you should also see if the lookup subquery can be rearranged into an inner / left join, i.e.
SELECT
...
REPLACE(TO_CHAR(C.I_WORKDATE, 'DD-Mon-YYYY'),
' ',
'') AS WORKDATE,
...
INNER JOIN T_EMPLOYEE_MS E
...
LEFT OUTER JOIN T_COMPENSATION C
ON C.I_COMPENSATEDDATE = A.I_REQDATE
AND C.I_EMPID = A.I_EMPID
...
(SELECT C.I_WORKDATE
FROM T_COMPENSATION C
WHERE C.I_COMPENSATEDDATE = A.I_REQDATE AND ROWNUM <= 1
AND C.I_EMPID = A.I_EMPID)

Returning Null Rows From Query

I was wondering if anyone could assist me with a DB2 query I'm working on?
I'm basically trying to do a left outer join between two tables DB2Cust and NOTIFICATION_REQUE on Control Number.
I want to return all rows in DB2Cust, however, in NOTIFICATION_REQUE I want to select the max Notice_DT and return that record only.
The below query is what I have so far.
It's close but it doesn't return control numbers in DB2Cust that don't exist in NOTIFICATION_REQUE.
SELECT
C.CONTROL_NO,
NR.CONTROL_NBR,
COALESCE(C.LNAME,'') AS LNAME,
COALESCE(C.FNAME,'') AS FNAME,
COALESCE(CAST(NR.NOTICE_DT AS VARCHAR(10)), '') AS NOTICE_DT
FROM WITC00DS.DB2CUST C
LEFT OUTER JOIN WITC00DS.NOTIFICATION_REQUE NR
ON C.CONTROL_NO = NR.CONTROL_NBR
AND C.AGENT_CODE = 'HR' AND C.STW_DATE BETWEEN '01/01/2000' AND '01/17/2014'
WHERE NR.NOTICE_DT = (SELECT MAX(NOTICE_DT)
FROM WITC00DS.NOTIFICATION_REQUE ZZ
WHERE ZZ.CONTROL_NBR = C.CONTROL_NO)
Here are two choices. You can move the subquery to the on clause:
SELECT C.CONTROL_NO, NR.CONTROL_NBR, COALESCE(C.LNAME,'') AS LNAME, COALESCE(C.FNAME,'') AS FNAME,
COALESCE(CAST(NR.NOTICE_DT AS VARCHAR(10)), '') AS NOTICE_DT
FROM WITC00DS.DB2CUST C LEFT OUTER JOIN
WITC00DS.NOTIFICATION_REQUE NR
ON C.CONTROL_NO = NR.CONTROL_NBR AND
C.AGENT_CODE = 'HR' AND
C.STW_DATE BETWEEN '01/01/2000' AND '01/17/2014' AND
NR.NOTICE_DT = (SELECT MAX(NOTICE_DT)
FROM WITC00DS.NOTIFICATION_REQUE ZZ
WHERE ZZ.CONTROL_NBR = C.CONTROL_NO
)
Or you can explicitly check for NULL in the `WHERE clause:
SELECT C.CONTROL_NO, NR.CONTROL_NBR, COALESCE(C.LNAME,'') AS LNAME, COALESCE(C.FNAME,'') AS FNAME,
COALESCE(CAST(NR.NOTICE_DT AS VARCHAR(10)), '') AS NOTICE_DT
FROM WITC00DS.DB2CUST C LEFT OUTER JOIN
WITC00DS.NOTIFICATION_REQUE NR
ON C.CONTROL_NO = NR.CONTROL_NBR AND
C.AGENT_CODE = 'HR' AND
C.STW_DATE BETWEEN '01/01/2000' AND '01/17/2014' AND
WHERE NR.NOTICE_DT is null or
NR.NOTICE_DT = (SELECT MAX(NOTICE_DT)
FROM WITC00DS.NOTIFICATION_REQUE ZZ
WHERE ZZ.CONTROL_NBR = C.CONTROL_NO
);