SQL Query Returns Duplicate Rows - sql

SELECT E.SSN AS SocialSecurityNumber ,
E.Name + ' ' + E.LastName AS FullName ,
J.Job ,
R.Date ,
R.STime AS StartTime,
R.ETime AS EndTime,
CASE WHEN R.Date BETWEEN O.SDate AND O.EDate THEN O.OffID
ELSE 0
END AS OffReason
FROM Resume AS R
INNER JOIN Employees AS E ON E.ID = R.EmpID
INNER JOIN Jobs AS J ON J.ID = E.JobID
LEFT JOIN Offs AS O ON E.ID = O.EmpID
AND R.Date BETWEEN O.SDate AND O.EDate
WHERE E.JobLeft = 0
AND R.Date BETWEEN '2014-11-26 00:00:00'
AND '2014-11-26 23:59:59'
ORDER BY FullName
I wrote this SQL Query for my employee resume report program. I want to retrieve when the employees start to work and if they have a reason for not coming to work, return the reasonId or if they don't retrieve 0. In this query, the result returns duplicate rows when there is two or more offreasons for the same employee. Like vacation & national holiday. I searched for this but can't find the same situation. The removed rows are not important; one offreason is enough for me. So I tried the DISTINCT keyword but it doesn't solve the problem. So what can I do to solve this?

The simplest would be to select only one of the reasons, using MAX for instance:
SELECT E.SSN AS SocialSecurityNumber,
E.Name + ' ' + E.LastName AS FullName,
J.Job,
R.DATE,
R.STime AS Start,
R.ETime AS END,
MAX(CASE
WHEN R.DATE BETWEEN O.SDate AND O.EDate THEN O.OffID
ELSE 0
END) AS OffReason
FROM Resume AS R
INNER JOIN Employees AS E
ON E.ID = R.EmpID
INNER JOIN Jobs AS J
ON J.ID = E.JobID
LEFT JOIN Offs AS O
ON E.ID = O.EmpID
AND R.DATE BETWEEN O.SDate AND O.EDate
WHERE E.Left = 0
AND R.DATE BETWEEN '2014-11-26 00:00:00' AND '2014-11-26 23:59:59'
GROUP BY E.SSN,
E.Name + ' ' + E.LastName,
J.Job,
R.DATE,
R.STime,
R.ETime
ORDER BY FullName

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;

Execute a query foreach row from a result set.

I have a query that needs to be executed for each row in a result set. I understand that I need to do this with cursors, but I dont know how.
The first query contains customers with a specific condition, I need to retreive the Id:s from them.
The second query selects the latest activity from these customers.
Query 1
SELECT DISTINCT c.Name, c.Id
FROM Persons p
JOIN Customers c ON c.id = p.Customer_Id
WHERE C.State = 3 AND c.SalesPerson_Id ='A3160011-CAA6-E411-A83E-AC7BA1B90A6D' AND c.Id NOT IN
(
SELECT Distinct p.Customer_Id
FROM Activities a
JOIN Persons p
ON a.Person_Id = p.Id
WHERE a.[Type] IN (5,6) AND a.[Time] > getdate()-30 AND a.[Time] < getdate()
)
AND c.id NOT IN
(
SELECT
DISTINCT p.Customer_Id
FROM
Persons p
JOIN HtmlEmails he ON p.Id = he.UserId
JOIN ReportLog rl ON he.Id = rl.HtmlEmails_Id
WHERE rl.[Time] > getdate()-30 AND rl.[Time] < getdate())
Query 2
SELECT TOP 1 *
FROM(
SELECT TOP 1 c.Name AS 'CustomerName', c.Id AS 'CustomerId', a.[Time] AS 'LastActivity',(p.FirstName + ' ' + p.LastName) AS 'UserFullName' , 'Login' AS 'Type'
FROM Activities a
JOIN Persons p ON p.Id = a.Person_Id
JOIN Customers c ON p.Customer_Id = c.Id
WHERE a.[Type] IN (5,6) AND a.[Time] < getdate()-30
ORDER BY a.[Time] DESC
UNION
SELECT TOP 1 c.Name AS 'CustomerName', c.Id AS 'CustomerId', a.[Time] AS 'LastActivity',(p.FirstName + ' ' + p.LastName) AS 'UserFullName' , 'Email' AS 'Type'
FROM Activities a
JOIN HtmlEmails he ON a.TargetId = he.Id
JOIN ReportLog rl on he.Id = rl.HtmlEmails_Id
JOIN Persons p ON p.Id = a.Person_Id
JOIN Customers c ON p.Customer_Id = c.Id
WHERE a.[Time] < getdate()-30
ORDER BY a.[Time] DESC
)AS x
ORDER BY 'LastActivity' DESC
Thanks!

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)

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.

extracting certain part of string in SQL stored procedure

I have this stored procedure at the moment. I would like to limit the String ChanceOfSuccess to only a the percentage like "40%". Right now the string reads something like this "40% - Thinking about it". What would be the best way to do this?
CREATE PROCEDURE [dbo].[procActivity_SelectbyOutstandingActivitiesNew]
AS
SELECT C.[Name] AS [Customer],
C.CustomerId AS [CustomerID],
E.FirstName + ' ' + E.Surname AS [Employee],
AT.[TypeName] AS [Activity Type],
A.[ActivityDate] AS ActivityDate,
A.NextActivityDate,
A.[ChanceOfSuccess] AS ChanceOfSuccess,
A.[Comments] AS Comments,
U.Surname + ' ' + U.FirstName AS [User],
E.EmployeeId,
A.ActivityId,
CONVERT(INT, SUBSTRING(A.ChanceOfSuccess, 0, CHARINDEX ('%',A.ChanceOfSuccess))) AS [Success Percentage],
A.[IsComplete]
FROM Customer C
INNER JOIN Activity A ON A.CustomerId = C.CustomerId
INNER JOIN Employee E ON E.EmployeeId = A.EmployeeId AND E.CustomerId = C.CustomerId
INNER JOIN [ActivityType] AT ON A.[ActivityTypeId] = AT.[ActivityTypeId]
INNER JOIN [User] U ON A.[UserId] = U.[UserId]
LEFT OUTER JOIN Activity A2 ON A.CustomerId = A2.CustomerId AND A.UserId = A2.UserId AND A2.ActivityDate > A.ActivityDate
WHERE C.[IsDeleted] = 0
AND A.NextActivityDate <= GETDATE()
AND E.IsDeleted = 0
AND A.IsDeleted = 0
AND (A2.ActivityId IS NULL OR A2.IsDeleted > 0)
AND CONVERT(INT, SUBSTRING(A.ChanceOfSuccess, 0, CHARINDEX ('%',A.ChanceOfSuccess))) < 100
ORDER BY A.NextActivityDate ASC, Customer, A.ActivityDate ASC, [Success Percentage] DESC
GO
Homework? you clearly didn't write the original query because it's already doing that work.
The line:
CONVERT(INT, SUBSTRING(A.ChanceOfSuccess, 0, CHARINDEX ('%',A.ChanceOfSuccess))) AS [Success Percentage],
Already does what you want and then converts it to an int, so remove the conversion and you should have what you want.
DECLARE #v VARCHAR(50) = '40% - Thinking about it'
SELECT #V, SUBSTRING(#V, 0, charindex('%', #V)+1)
Here are some surgestions to how to rewrite your code:
create view [v_OutstandingActivities]
AS
SELECT C.[Name] AS [Customer],
C.CustomerId AS [CustomerID],
E.FirstName + ' ' + E.Surname AS [Employee],
AT.[TypeName] AS [ActivityType],
A.[ActivityDate] AS ActivityDate,
A.NextActivityDate,
A.[ChanceOfSuccess] AS ChanceOfSuccess,
A.[Comments] AS Comments,
U.Surname + ' ' + U.FirstName AS [UserName],
E.EmployeeId,
A.ActivityId,
replace(A.ChanceOfSuccess, '%', '')+0 AS [SuccessPercentage],
A.[IsComplete]
FROM Customer C
INNER JOIN Activity A ON A.CustomerId = C.CustomerId
INNER JOIN Employee E ON E.EmployeeId = A.EmployeeId AND E.CustomerId = C.CustomerId
INNER JOIN [ActivityType] AT ON A.[ActivityTypeId] = AT.[ActivityTypeId]
INNER JOIN [User] U ON A.[UserId] = U.[UserId]
LEFT OUTER JOIN Activity A2 ON A.CustomerId = A2.CustomerId AND A.UserId = A2.UserId
AND A2.ActivityDate > A.ActivityDate AND (A2.IsDeleted > 0)
WHERE C.[IsDeleted] = 0
AND A.NextActivityDate <= GETDATE()
AND E.IsDeleted = 0
AND A.IsDeleted = 0
AND replace(A.ChanceOfSuccess, '%', '') < 100