How to SELECT unique rows from 3 JOINED tables - sql-server-2005

I am trying to check if a persons name appears multiple times in a table aswell as pull additional information (position title, department name, employee number) to limit creating two stored procedures 1. to check if name appears multiple times and 2. to get the additional information after check has been completed.
my current query right now is as follows, which returns multiple rows as the ID I am using to join the tables appears multiple times in one table (an employee can belong to multiple departments, the EmpID occurs here multiple times thyus returning the multiple values).
SELECT c.FirstName+ ' ' + c.LastName as emp_full_name, e.EmployeeNumber,
e.EmpID, dh.PositionTitle, d.Name as deptName, e.isActive
FROM Person.Contact c
INNER JOIN HumanResources.Employee e ON c.ContactID = e.ContactID
INNER JOIN HumanResources.EmployeeDepartmentHistory dh ON e.EmpID = dh.EmpID
INNER JOIN HumanResources.Department d ON dh.DepartmentID = d.DepartmentID
WHERE c.FirstName+ ' ' + c.LastName LIKE #empName
My general table structure is as follows:
Person.Contact
ContactID
FirstName
LastName
HumanResources.Employee
EmpID
EmployeeNumber
isActive
HumanResources.EmployeeDepartmentHistory
DepartmentHistoryId
EmpID
DepartmentID
PositionTitle
HumanResources.Department
DepartmentID
Name
Example Result Set:
John Doe 1234567 1 Database Architect Administration A
John Doe 1234567 1 Database Tester Administration A

Use Distinct keyword
SELECT distinct c.FirstName+ ' ' + c.LastName as emp_full_name, e.EmployeeNumber,
e.EmpID, STUFF((
SELECT ',' + edh.PositionTitle
FROM dbo.EmployeeDepartmentHistory edh
WHERE edh.EmpID = e.EmpID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''), e.isActive
FROM Person.Contact c
INNER JOIN HumanResources.Employee e ON c.ContactID = e.ContactID
INNER JOIN HumanResources.EmployeeDepartmentHistory dh ON e.EmpID = dh.EmpID
INNER JOIN HumanResources.Department d ON dh.DepartmentID = d.DepartmentID
WHERE c.FirstName+ ' ' + c.LastName LIKE #empName
Group By e.EmpID

Related

Join tables and Concatenate Multiple Rows Within Single Row

Join multiple table and concatenate multiple rows into one for distinct department names.
I was able to do it on table EmpDepartment but I need to join all table.
Expected output like this.
Need Query
EmpID
EmpName
DepartmentName
1
A
q,w,e
2
B
Null
SELECT
p.EmpID,
EmpName,
STRING_AGG(DepartmentName,',') as DepartmentName
FROM
EmpPayroll p
LEFT OUTER JOIN
EmpDepartment e on e.EmpID=p.EmpID
LEFT OUTER JOIN
Department d on d.deptID=e.deptID
GROUP BY
p.EmpID,
EmpName
if you're on SQL Server 2017 or later.
https://learn.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-ver15
I updated as per your request.
If you are using SQL Server 2017 and above Versions
SELECT ED.EmpId
,EP.EmpName
,STRING_AGG(D.DepartmentName,',') AS DepartmentName
FROM EmpPayroll EP
LEFT JOIN EmpDepartment ED
ON EP.EmpId = ED.EmpId
LEFT JOIN Department D
ON D.DeptId = ED.DeptId
Group By ED.EmpId, EP.EmpName
and for SQL Server 2016 and Below Versions
;WITH Partitioned AS
(
SELECT EP.EmpId, EP.EmpName,D.DepartmentName,
ROW_NUMBER() OVER (PARTITION BY ED.EmpId ORDER BY D.DepartmentName) AS NameNumber,
COUNT(*) OVER (PARTITION BY ED.EmpId) AS NameCount
FROM EmpPayroll EP
LEFT JOIN EmpDepartment ED
ON EP.EmpId = ED.EmpId
LEFT JOIN Department D
ON D.DeptId = ED.DeptId
),
Concatenated AS
(
SELECT
EmpId,
CAST(DepartmentName AS nvarchar) AS DepartmentName,
EmpName,
NameNumber,
NameCount
FROM Partitioned
WHERE NameNumber = 1
UNION ALL
SELECT
P.EmpId,
CAST(P.DepartmentName + ', ' + C.DepartmentName AS nvarchar),
P.EmpName,
P.NameNumber,
P.NameCount
FROM Partitioned AS P
INNER JOIN Concatenated AS C
ON P.EmpId = C.EmpId
AND P.NameNumber = C.NameNumber + 1
)
SELECT
EmpId,
EmpName,
DepartmentName
FROM Concatenated
WHERE NameNumber = NameCount

SQL query to show Name and Department in a table

Can you please help me, I don't know how to create this query. I am a front end dev.
Expected result should be table with 2 columns
Name(one) DepartmentName(many)
The tables and their relationship are shown in this image:
You can do this with a couple of INNER JOINS. You don't need to reference the Location table in this query because Employee.LocationId is the same as EmployeeDepartment.LocationId.
This simple query will return all employee names and all department names they are related to. The employee name may be repeated, as this query puts only one department name in the column, so if an employee is in two departments, you get the employee name twice.
SELECT
EmployeeName = e.Name
,DepartmentName = d.Name
FROM Employee e
INNER JOIN EmployeeDepartment ed ON ed.LocationId = e.LocationId
INNER JOIN Department d ON d.id = ed.DepartmentId
This query is a bit more complicated, but returns each employee name only once, and the department names will be a comma-separated string of names. This is accomplished by using STUFF() in conjunction with FOR XML.
;WITH EmployeesAndDepartments AS
(
SELECT
EmployeeName = e.Name
,DepartmentName = d.Name
FROM Employee e
INNER JOIN EmployeeDepartment ed ON ed.LocationId = e.LocationId
INNER JOIN Department d ON d.id = ed.DepartmentId
)
SELECT
ead.EmployeeName
,Departments = STUFF((
SELECT ',' + DepartmentName
FROM EmployeesAndDepartments
FOR XML PATH('')
) , 1, 1, ''
)
FROM EmployeesAndDepartments ead
GROUP BY ead.EmployeeName
This should work
SELECT
e.Name
, d.Name AS DepartmentName
FROM
Employee e
LEFT OUTER JOIN
EmployeeDepartment ed
ON e.LocationId = ed.LocationId
LEFT OUTER JOIN
Department d
ON ed.DepartmentId = d.id
Note that the use of LEFT OUTER JOIN will return the employee even if they don't have a corresponding record in EmployeeDeparatment and/or Department. If you only want to retrieve employees that do have corresponding EmployeeDepartment and Department records, use INNER JOIN instead

Subquery with more than one column

I try to build a subquery with more than one column. Like this:
--SELF JOIN:
WITH Employees AS
(
SELECT
e.EmployeeID, e.ManagerID, e.Title,
c.FirstName + ISNULL(' ' + c.MiddleName,'') + ' ' + c.LastName AS EmpName
FROM
Employee AS e
INNER JOIN
Contact AS c ON e.ContactID = c.ContactID
)
SELECT
emp.EmployeeID, emp.ManagerID, emp.EmpName, emp.Title AS EmpTitle,
mgr.EmpName as MgrName, mgr.Title as MgrTitle
FROM
Employees AS Emp
INNER JOIN
Employees AS Mgr ON Emp.ManagerID = Mgr.EmployeeID;
--2
WITH Employees AS
(
SELECT
e.EmployeeID, e.ManagerID, e.Title,
c.FirstName + ISNULL(' ' + c.MiddleName,'') + ' ' + c.LastName AS EmpName
FROM
Employee AS e
INNER JOIN
Contact AS c ON e.ContactID = c.ContactID
)
SELECT
EmployeeID, ManagerID, EmpName, Title
FROM
Employees
WHERE
EmployeeID IN (SELECT EmployeeID, er2.MaritalStatus
FROM Employees AS e
INNER JOIN AdventureWorks2012.HumanResources.Employee AS er2 ON e.ManagerID = er2.BusinessEntityID
WHERE er2.MaritalStatus = 'M');
I want also to show the MarialStatus. But I can't do it this way, because I get the error:
Only one expression can be specified in the select list when the
subquery is not introduced with EXISTS.
But If I can't do it with exists in the subquery. So my question is: what is the proper way that I can select more then one column in the subquery?
Thank you
You can't return 2 values in a sub query.So you will have to remove the MaritalStatus column. That's the problem with your query.
USE tempdb;
WITH
Employees AS(
SELECT e.EmployeeID, e.ManagerID,e.Title,
c.FirstName + ISNULL(' ' + c.MiddleName,'') + ' ' + c.LastName AS EmpName
FROM Employee AS e
INNER JOIN Contact AS c
ON e.ContactID = c.ContactID
)
SELECT emp.EmployeeID, emp.ManagerID, emp.EmpName, emp.Title AS EmpTitle,
mgr.EmpName as MgrName, mgr.Title as MgrTitle
FROM Employees AS Emp INNER JOIN Employees AS Mgr
ON Emp.ManagerID = Mgr.EmployeeID;
--2
WITH Employees AS (
SELECT e.EmployeeID, e.ManagerID,e.Title,
c.FirstName + ISNULL(' ' + c.MiddleName,'') + ' ' + c.LastName AS EmpName
FROM Employee AS e
INNER JOIN Contact AS c
ON e.ContactID = c.ContactID)
SELECT e.EmployeeID, e.ManagerID, e.EmpName, e.Title,er2.MaritalStatus
FROM Employees e
INNER JOIN AdventureWorks2012.HumanResources.Employee AS er2
ON e.ManagerID = er2.BusinessEntityID
WHERE er2.MaritalStatus = 'M');
I don't think you need a subquery there,because INNER JOIN will give you only records if there's a match. So do a INNER JOIN fro the table and put the MaritalStatus column in the select clause
The problem appears to be in your second query. The second column is not necessary in the SELECT. But, you can further simplify the logic by removing the JOIN:
SELECT e.EmployeeID, e.ManagerID, e.EmpName, e.Title
FROM Employees e
WHERE e.ManagerID IN (SELECT er2.BusinessEntityID
FROM AdventureWorks2012.HumanResources.Employee er2
WHERE er2.MaritalStatus = 'M'
);

Recursive CTE query in SQL Server

I've looked at many recursive CTE examples on this site already and I've tried applying this to my own data. I seem to have a few more joins than most examples but I think I'm getting there. The problem I want help with is that I get an error saying:
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
I have run the first select statement of the CTE and I've repeatedly run the second select statement (with different actual position codes) and for this employee there are six levels so I can't see why I get the error.
My one area of suspicion is that I have to apply the criteria 'relationship_type_id = 0' as this is the 'Reports To' relationship type. I had tried making this a left outer join but they aren't allowed. This means I don't get the top level manager using this query (i.e. the person with no manager themselves).
I've posted the code below and any help would be much appreciated.
WITH EmployeeHierarchy (EmployeeID, LastName, FirstName, PositionCode, ReportsTo, HierarchyLevel) AS
(
SELECT
p.employee_id as EmployeeID,
p.last_name as LastName,
p.first_name as FirstName,
pos.position_code as PositionCode,
r.to_position_code as ReportsTo,
1 as HierarchyLevel
FROM
--JOIN: Personal details
dbo.person p
--JOIN: Employment links a person to a post (could have more than one)
INNER JOIN
dbo.employment e ON e.employee_id = p.employee_id
--JOIN: details of the position held
INNER JOIN
dbo.position pos ON pos.position_code = e.position_code
--JOIN: Relationships between the positions, one position reports to another position etc.
-- There are several 'relationship types', we are only interested in relationship_type_id = 0
-- as this is the 'Reports to' relationship code. Others types include 'Managed by' etc.
INNER JOIN
dbo.relationship r ON r.from_position_code = pos.position_code AND r.relationship_type_id = 0
WHERE
--CRITERIA: Use my employee Id as a starting point for testing
p.employee_id = '10076395'
UNION ALL
-- Recursive step
SELECT
p2.employee_id as EmployeeID,
p2.last_name as LastName,
p2.first_name as FirstName,
pos2.position_code as PositionCode,
r2.to_position_code as ReportsTo,
eh.HierarchyLevel + 1 AS HierarchyLevel
FROM
dbo.person p2
INNER JOIN
dbo.employment e2 ON e2.employee_id = p2.employee_id
INNER JOIN
dbo.position pos2 ON pos2.position_code = e2.position_code
INNER JOIN
dbo.relationship r2 ON r2.from_position_code = pos2.position_code AND r2.relationship_type_id = 0
--JOIN: Link this query back to the base query
INNER JOIN
EmployeeHierarchy eh ON r2.from_position_code = eh.PositionCode
)
SELECT *
FROM EmployeeHierarchy
ORDER BY HierarchyLevel, LastName, FirstName
Give this a try.
I've moved the complex joins to a separate CTE to keep the recursive section as clean and as simple as possible. If nothing else this approach should help you pinpoint your issue.
; WITH employees AS (
SELECT person.employee_id
, person.last_name
, person.first_name
, position.position_code
, relationship.to_position_code
FROM dbo.person
INNER
JOIN dbo.employment
ON employment.employee_id = person.employee_id
INNER
JOIN dbo.position
ON position.position_code = employment.position_code
INNER
JOIN dbo.relationship
ON relationship.from_position_code = position.position_code
AND relationship.relationship_type_id = 0
)
, recursive_bit AS (
SELECT employee_id
, last_name
, first_name
, position_code
, to_position_code
FROM employees
WHERE employee_id = '10076395'
UNION ALL
SELECT employees.employee_id
, employees.last_name
, employees.first_name
, employees.position_code
, employees.to_position_code
FROM recursive_bit
INNER
JOIN employees
ON employees.position_code = recursive_bit.to_position_code
)
SELECT *
FROM recursive_bit
Please try :
WITH EmployeeHierarchy (EmployeeID, LastName, FirstName, PositionCode, ReportsTo, HierarchyLevel) AS
(
SELECT
p.employee_id as EmployeeID,
p.last_name as LastName,
p.first_name as FirstName,
pos.position_code as PositionCode,
r.to_position_code as ReportsTo,
1 as HierarchyLevel
FROM
--JOIN: Personal details
dbo.person p
--JOIN: Employment links a person to a post (could have more than one)
INNER JOIN dbo.employment e
ON e.employee_id = p.employee_id
--JOIN: details of the position held
INNER JOIN dbo.position pos
ON pos.position_code = e.position_code
--JOIN: Relationships between the positions, one position reports to another position etc.
-- There are several 'relationship types', we are only interested in relationship_type_id = 0
-- as this is the 'Reports to' relationship code. Others types include 'Managed by' etc.
INNER JOIN dbo.relationship r
ON r.from_position_code = pos.position_code
and r.relationship_type_id = 0
WHERE
--CRITERIA: Use my employee Id as a starting point for testing
p.employee_id = '10076395'
UNION ALL
-- Recursive step
SELECT
p2.employee_id as EmployeeID,
p2.last_name as LastName,
p2.first_name as FirstName,
pos2.position_code as PositionCode,
r2.to_position_code as ReportsTo,
eh.HierarchyLevel + 1 AS HierarchyLevel
FROM
dbo.person p2
INNER JOIN dbo.employment e2
ON e2.employee_id = p2.employee_id
INNER JOIN dbo.position pos2
ON pos2.position_code = e2.position_code
INNER JOIN dbo.relationship r2
ON r2.from_position_code = pos2.position_code
and r2.relationship_type_id = 0
--JOIN: Link this query back to the base query
INNER JOIN EmployeeHierarchy eh
ON r2.from_position_code = eh.ReportsTo
)
SELECT *
FROM EmployeeHierarchy
ORDER BY HierarchyLevel, LastName, FirstName

SQL Server 2005 - Nested recursive query :(

I have a query that I need to execute that I do not know how to structure.
I have a table called Employees. I have another table called Company. There is a third table called Files. As you can imagine, a Company has Employees, and Employees have Files.
I need to list out all of the Employees in my database. The challenge is, I need to list the total number of Files in the same company as the Employee. I have tried variations on the following without any luck:
SELECT
e.FirstName,
e.LastName,
e.Company,
(SELECT COUNT(*) FROM Files f WHERE f.EmployeeID IN (SELECT [ID] FROM Employees e2 WHERE e2.CompanyID=e.CompanyID)) as 'FileCount'
FROM
Employees e
What am I doing wrong? Thank you!
Try this:
SELECT
e.FirstName,
e.LastName,
e.Company,
(
SELECT COUNT(*)
FROM Files f
JOIN Employees e2 ON f.EmployeeID = e2.id
WHERE e2.CompanyID = e.CompanyID
) as 'FileCount'
FROM
Employees e
There are a lot of ways to get that. If the performance is a concern, this is more optimal according to estimated execution plan costs.
SELECT
e.FirstName,
e.LastName,
e.Company,
COUNT(f.FileId)
FROM
Employees e
INNER JOIN Files f ON e.EmployeeID = f.EmployeeID
GROUP BY
e.FirstName,
e.LastName,
e.Company
A solution with no correlation in SELECT clause. Probably quicker...
SELECT
e.FirstName,
e.LastName,
e.Company,
foo.FileCount
FROM
Employees e
JOIN
(
SELECT
COUNT(*) AS FileCount, --OR COUNT(DISTINCT something) ?
e2.Company, f.EmployeeID
FROM
Files f JOIN Employees e2 ON f.EmployeeID = e2.id
GROUP BY
e2.Company, f.EmployeeID
) foo ON e.Company = foo.Company AND e.id = foo.EmployeeID
How about:
SELECT
e.FirstName,
e.LastName,
e.Company,
select count(*) from Files f, Employees e where f.EmployeeID=e.EmployeeID and e.CompanyID=emp.CompanyID
FROM
Employees emp
WITH FilesPerCompany (CompanyID, NumberOfFiles)
AS (SELECT c.ID AS CompanyID,
COUNT(*) AS NumberOfFiles
FROM Companies c
INNER JOIN Employees e ON c.ID = e.CompanyID
INNER JOIN Files f ON e.ID = f.EmployeeID
GROUP BY c.ID
)
SELECT e.FirstName,
e.LastName,
e.Company,
COALESCE(s.NumberOfFiles, 0) AS NumberOfFilesPerCompany
FROM Employees e
LEFT JOIN FilesPerCompany s
ON s.CompanyID = e.CompanyID
The following statement uses recursive joins to iterate down employees who manage other employees who manage other employees .... etc. Our structure is a little convoluted as the management structure is role based which actually allows an employee to have more than 1 manager. You can add a reference to Files within this recursion.
WITH Manager as
(SELECT c.Forenames + ' ' + c.Surname as Employee,
c2.Forenames + ' ' + c2.Surname AS Manages,
c.accountid AS AccountID, c.[Status] AS [Status]
FROM [intranet].[dbo].[tblContact] c
LEFT JOIN tblContactPost cp ON cp.contactid = c.contactid
LEFT JOIN tblPost p ON p.ParentRoleId = cp.RoleID AND p.ParentPostArea = cp.PostArea AND p.ParentPostNo = cp.PostNo
INNER JOIN tblContactPost cp2 ON cp2.RoleId = p.RoleId AND cp2.PostArea = p.PostArea AND cp2.PostNo = p.PostNo
INNER JOIN tblContact c2 ON c2.ContactID = cp2.ContactId
)
,jn AS
(SELECT Employee, Manages
FROM Manager
Where AccountID = 'ad\lgardner' AND [Status] = 'A'
UNION ALL
SELECT c.Employee, c.Manages
FROM jn as p JOIN Manager AS c
ON c.Employee = p.Manages
)
SELECT jn.Employee, jn.Manages
From jn
Order BY 1