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

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)

Related

How to write one CASE function two THEN method at the same time END AS Two NEW column depend on THEN action

SELECT Emp.IntegrationFieldOne AS LocationCode, Emp.EmployeeId,
Emp.FirstName + ' '+ Emp.LastName as 'EmployeeName',
CASE WHEN ( (ORSet.Rate) =1.5 ) THEN
Sum(OT.Minutes)
END AS '1.5HRSMinutes',
CASE WHEN( ORSet.Rate =2 ) THEN
Sum(OT.Minutes)
END AS '2HRSMinutes'
FROM [CemexDB_CP_Test].[TimeAttendance].[OvertimeTransaction] OT
INNER JOIN [HumanResource].[Employee] Emp ON OT.EmployeeId = Emp.EmployeeId
INNER JOIN [CemexDB_CP_Test].[TimeAttendance].[OvertimeRateSettingDetail]
ORSet ON ORSet.OvertimeRateSettingId= OT.OvertimeType
GROUP BY Emp.EmployeeId,Emp.FirstName,Emp.LastName,Emp.IntegrationFieldOne,ORSet.Rate[!
This is Output picture click it]1
This is the out put but i want same row depend same id. but here each id has 2
row how to handle it
I think you want conditional aggregation:
SELECT Emp.IntegrationFieldOne AS LocationCode, Emp.EmployeeId,
Emp.FirstName + ' '+ Emp.LastName as EmployeeName,
SUM(CASE WHEN ORSet.Rate = 1.5 THEN OT.Minutes END) AS [1.5HRSMinutes],
SUM(CASE WHEN ORSet.Rate = 2 THEN OT.Minutes END) AS [2HRSMinutes]
FROM [CemexDB_CP_Test].[TimeAttendance].[OvertimeTransaction] OT INNER JOIN
[HumanResource].[Employee] Emp
ON OT.EmployeeId = Emp.EmployeeId INNER JOIN
[CemexDB_CP_Test].[TimeAttendance].[OvertimeRateSettingDetail]
ORSet
ON ORSet.OvertimeRateSettingId= OT.OvertimeType
GROUP BY Emp.EmployeeId, Emp.FirstName, Emp.LastName, Emp.IntegrationFieldOne;

SQL Query with Left Join slows down

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

WHERE Clause with conditions in it

I want to create a where clause that will take into account the termination date and say if they hire date is between these values and the term date isn't on or before the hiredate - IE the employee never started - how would I go about that? So far I have this:
SELECT A.AdpID AS EmployeeID,
ISNULL(A.Lname, '') AS [Last Name],
ISNULL(A.Fname, '') AS [First Name],
ISNULL(A.PrimaryEmail, '') AS [Email],
ISNULL(M.Fname + ' ' + M.Lname, '') AS Manager,
ISNULL(CONVERT(VARCHAR(100), A.HireDate, 101), '') AS HireDate,
ISNULL(CONVERT(VARCHAR(100), A.TerminationDate, 101), '') AS TermDate,
ISNULL(DIV.DivisionName, '') AS Division,
ISNULL(FUN.FunctionName, '') AS [Function],
ISNULL(DEP.DepartmentName, '') AS Department,
ISNULL(WGP.WorkgroupName, '') AS Workgroup,
ISNULL(LOB.CcaLOBName, '') AS LOB,
ISNULL(MAES.EmpStatusDesc, '') AS [Employee Type]
FROM dbo.Associates AS A
LEFT OUTER JOIN dbo.Associates AS M -- Look up for associate information
ON A.SuperPrincipal = M.AssocId
LEFT OUTER JOIN dbo.MasterCCALob AS LOB -- Look up line of business
ON A.LobID = LOB.CcaLOBID
LEFT OUTER JOIN dbo.MasterAssocEmpStatus AS MAES -- Look up for employee type
ON A.EmpStatusID = MAES.EmpStatusID
LEFT OUTER JOIN dbo.AssociatesDepartment AS DEP WITH(NOLOCK) -- Look up for Department
ON A.AssociatesDepartmentID = DEP.AssociatesDepartmentID
LEFT OUTER JOIN dbo.AssociatesDivision AS DIV WITH(NOLOCK) -- Look up for Division
ON A.AssociatesDivisionID = DIV.AssociatesDivisionID
LEFT OUTER JOIN dbo.AssociatesWorkGroup AS WGP WITH(NOLOCK) -- Look up for WorkGroup
ON A.AssociatesWorkgroupID = WGP.AssociatesWorkgroupID
LEFT OUTER JOIN dbo.AssociatesFunction AS FUN WITH(NOLOCK) -- Look up for Function
ON A.AssociatesFunctionID = FUN.AssociatesFunctionID
WHERE ( LEN(A.TerminationDate) = 0
AND ISNULL(A.HireDate, '1900-01-01') BETWEEN '2015-10-01' AND GETDATE() )
OR ( LEN(A.TerminationDate) > 0
AND ISNULL(A.TerminationDate, '1900-01-01') > ISNULL(A.HireDate, '1900-01-01')
AND ISNULL(A.HireDate, '1900-01-01') BETWEEN '2015-10-01' AND GETDATE() )
ORDER BY A.HireDate DESC
There where clause is as bad as can be because I get the craziest results back from this

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
);

how to limit row number to one unique row in SQL query?

I need to limit the row number to one unique row in SQL query. Here's sample data to recognize what I'm talking about:
john doe 3000 fog horn drive , ky 40444
john doe 3001 merry lane , ky 40484
I want to return the first one in the list here's my query :
Select
DISTINCT p.personID, e.citizenship,
rtrim(i.lastname + CASE WHEN i.suffix IS NULL THEN '' ELSE ' ' + i.suffix END) + ', ' + i.firstname + (CASE WHEN i.middlename IS NULL THEN '' ELSE ' ' + i.middlename END) StuName,
e.grade, i.gender, p.studentNumber, e.citizenship, e.adult, i.birthdate,
e.disability1, e.disability2, ad.city, e.displacedHomemaker, e.homeSchooled,
e.localStudentNumber, e.migrant, e.modifiedDate, e.modifiedByID,
rtrim(Staff.lastname + CASE WHEN Staff.suffix IS NULL THEN '' ELSE ' ' + Staff.suffix END) + ', ' + Staff.firstname + (CASE WHEN Staff.middlename IS NULL THEN '' ELSE ' ' + Staff.middleName END) Staffname,
Staff.personID Staffid, i.lastname, i.firstname, i.middlename, i.ssn,
ad.phone, ad.state, ad.zip, ad.addressLine1
FROM
Person p
LEFT join
Enrollment e ON e.personID = p.personID And isnull(e.noshow, 0) = 0
LEFT join
EnrollmentKY ky ON ky.enrollmentID = e.enrollmentID
LEFT join
[Identity] i ON i.identityID = p.currentIdentityID And i.personID = p.personID
INNER join
Calendar c ON c.calendarID = e.calendarID
INNER join
SchoolYear sy ON sy.endYear = c.endYear AND sy.active = 1
JOIN
staffMember Staff ON Staff.personID = e.modifiedByID
--join view_students s ON s.personID = i.personID
left join
v_MailingAddress ad ON ad.personID = i.personID And ad.relatedBy = 'household'
And ad.endDate IS NULL And isnull(ad.secondary, 0) = 0
order by
i.lastname, i.firstname, i.middlename
edit: need to only pick first row in SQL code because I have a problem with people that have multiple addresses it puts two rows for them and i only need first row of data for the person that has multiple addresses.
If the personId is distinct for each of the records and they just have a different address, then you can add a field for the row_number() and then only select the records where the row_number = 1:
select *
from
(
Select p.personID,
e.citizenship,
rtrim(i.lastname + CASE WHEN i.suffix IS NULL THEN '' ELSE ' ' + i.suffix END) + ', ' + i.firstname + (CASE WHEN i.middlename IS NULL THEN '' ELSE ' ' + i.middlename END) StuName,
e.grade,
i.gender,
p.studentNumber,
e.citizenship,
e.adult,
i.birthdate,
e.disability1,
e.disability2,
ad.city,
e.displacedHomemaker,
e.homeSchooled,
e.localStudentNumber,
e.migrant,
e.modifiedDate,
e.modifiedByID,
rtrim(Staff.lastname + CASE WHEN Staff.suffix IS NULL THEN '' ELSE ' ' + Staff.suffix END) + ', ' + Staff.firstname + (CASE WHEN Staff.middlename IS NULL THEN '' ELSE ' ' + Staff.middleName END) Staffname,
Staff.personID Staffid,
i.lastname,
i.firstname,
i.middlename,
i.ssn,
ad.phone,
ad.state,
ad.zip,
ad.addressLine1,
row_number() over(partition by p.personid order by p.personid) rn -- add this field
FROM Person p
LEFT join Enrollment e
ON e.personID = p.personID
And isnull(e.noshow,0)=0
LEFT join EnrollmentKY ky
ON ky.enrollmentID = e.enrollmentID
LEFT join [Identity] i
ON i.identityID = p.currentIdentityID
And i.personID = p.personID
INNER join Calendar c
ON c.calendarID = e.calendarID
INNER join SchoolYear sy
ON sy.endYear = c.endYear
AND sy.active = 1
JOIN staffMember Staff
ON Staff.personID = e.modifiedByID
--join view_students s ON s.personID = i.personID
left join v_MailingAddress ad
ON ad.personID = i.personID
And ad.relatedBy = 'household'
And ad.endDate IS NULL
And isnull(ad.secondary,0)=0
) x
where x.rn = 1
order by x.lastname, x.firstname, x.middlename
Try using the LIMIT to limit the no. of outputs.
Eg:
SELECT COLUMN_NAME
FROM TABLE
ORDER BY CONDITION
LIMIT NO_OF_ROWS;
Have you tried using a "GROUP BY" clause instead of the DISTINCT keyword?
Also, what about a Sub-Query? If I were writing this type of thing I'd use a sproc and create a temporary table.
Edit: Deleted original answer as question was changed and original answer is not the way to go with changed question.
Suggest GROUP BY clause per Neil Hoskins.