Join tables and Concatenate Multiple Rows Within Single Row - sql

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

Related

How to show the employess who have only one project, and also their project. Many to Many relationship problem

Project Table:
Employee Table
Connector Table:
Therea are 6 employees who have only one project
one of the prjects is P2, it has 5 employees
and the other project is P3 which has 1 employee
Select * from Emp
Select * from Project
Select * from PrEmpConnector
------------------------------------------------
Select t.EmpId as Employee_Id, t.Project_Name
from (
Select
PE.EmpId,
P.Project_Name
from PrEmpConnector as PE
inner join Project as P on P.Id in(
Select sum(t.ProjectId)
from PrEmpConnector as t
group by t.EmpId
having COUNT(t.EmpId) = 1
)
) as t
group by t.Project_Name, t.EmpId
having t.EmpId in (
Select t.EmpId
from PrEmpConnector as t
group by t.EmpId
having COUNT(*) = 1
)
select emp.name,emp.surname,project.project_name from
PrEmpConnector
inner join emp on emp.id=PrEmpConnector.emp_id
inner join Project on Project.id=PrEmpConnector.projectid
where emp.empid in (
select emp_id from PrEmpConnector
group by emp_id having count(projectid) =1)
If you are looking for employees that only work on one project, you can join the three tables, aggregate by employee, and filter on groups that contain only one record with a having clause:
select
e.id employee_id,
e.name employee_name,
min(p.id) project_id,
min(p.project_name) project_name
from project p
inner join connector c on c.projectId = p.id
inner join employee e on e.id = c.employeeId
group by e.id, e.name
having count(*) = 1

Query to get employee of different departments

I have 3 tables: Employee, Department and employeeProject.
The relation between employee and employeeproject is one-to-many. The relation between employee and department is many-to-one.
I want to write a query to select 10 employees who have worked in projects 3 and 4. The query should return employees of different departments if possible.
The query below kind of works. The only problem is that the relationship between employee and employeeproject is one-to-many, so it might return the same employee number multiple times.
I cannot use distinct because all fields in the order by clause should be used in select when using distinct.
select top 10 empid from employee e
inner join department d on d.depId=e.depid
inner join employeeProject p on p.empid=e.empid
where p.projectID in (3,4)
order by row_number() over(partition by e.depId order by e.empid)
Bit of a guess, but use an EXISTS?
SELECT TOP 10 e.empid
FROM employee e
JOIN department d ON e.depid = d.depid
WHERE EXISTS (SELECT 1
FROM employeeproject p
WHERE p.emdid = e.empid
AND p.projectid IN (3,4))
ORDER BY e.depid, e.empid;
I suggest aggregating by employee, and then using an assertion the HAVING clause:
SELECT TOP 10 e.empid
FROM employee e
INNER JOIN department d
ON d.depId = e.depid
INNER JOIN employeeProject p
ON p.empid = e.empid
WHERE
p.projectID IN (3,4)
GROUP BY
e.empid
HAVING
MIN(p.projectID) <> MAX(p.projectID);
If the minimum and maximum projectID are not equal for a given employee, after restricting to only projects 3 and 4, then it implies that this employee meets the criteria.
Why not just use select distinct?
select distinct top 10 empid
from employee e inner join
employeeProject p
on p.empid = e.empid
where p.projectID in (3, 4)
order by row_number() over (partition by e.depId order by e.empid);
Note that the department table is not needed.
Alternatively,
select top (10) e.*
from employee e
where exists (select 1
from employeeprojects ep
where p.emdid = e.empid and
p.projectid in (3, 4)
)
order by row_number() over (partition by e.depid order by newid());

Find distinct values from the generated result set in SQL

Two select statements union together with multiple inner joins.
Just need to find out the distinct elements out of this result sets.
I could have write distinct when selecting columns but this is not what I want.
SELECT
employeeid, employeename
FROM
employee AS emp
INNER JOIN
department AS dep ON emp.employeeid = dep.employeeid
INNER JOIN
company AS comp ON emp.companyid = comp.companyid
UNION
SELECT
employeeid, employeename
FROM
employer AS emp
INNER JOIN
category AS cat ON emp.employeeid = cat.employeeid
INNER JOIN
business AS busi ON emp.companyid = busi.companyid
SELECT DISTINCT employeeid, employeename
FROM
(
SELECT
employeeid, employeename
FROM
employee AS emp
INNER JOIN
department AS dep ON emp.employeeid = dep.employeeid
INNER JOIN
company AS comp ON emp.companyid = comp.companyid
UNION
SELECT
employeeid, employeename
FROM
employer AS emp
INNER JOIN
category AS cat ON emp.employeeid = cat.employeeid
INNER JOIN
business AS busi ON emp.companyid = busi.companyid
) AS t

Getting individual counts of last three distinct rows in column of data retrieved from multiple tables

I have a query which returns several rows of data (in datetime format) of a single column obtained by performing JOINS on multiple SQL Tables. The Data obtained is a DateTime type and now I just want the individual count of latest three dates probably the count of lat three distinct dates as it sorted from earliest to latest.
SQL Query
SELECT
ST.EffectiveDate
FROM Person.Contact C
INNER JOIN Sales.SalesPerson SP
ON C.ContactID = SP.SalesPersonID
FULL OUTER JOIN Sales.SalesTerritory ST
ON ST.TerritoryID = SP.TerritoryID
The above query returns around 200 rows of data but I want the count for each of three latest dates possibly bottom three
I would do this with top and group by:
SELECT TOP 3 ST.EffectiveDate, COUNT(*) as cnt
FROM Person.Contact C INNER JOIN
Sales.SalesPerson SP
ON C.ContactID = SP.SalesPersonID FULL OUTER JOIN
Sales.SalesTerritory ST
ON ST.TerritoryID = SP.TerritoryID
GROUP BY ST.EffectiveDate
ORDER BY ST.EffectiveDate DESC
added another query to get the latest 3 distinct dates
SELECT count(1)
FROM Person.Contact C
INNER JOIN Sales.SalesPerson SP
ON C.ContactID = SP.SalesPersonID
FULL OUTER JOIN Sales.SalesTerritory ST
ON ST.TerritoryID = SP.TerritoryID
WHERE ST.effectivedate in (select distinct top 3 effectivedate
from salesterritory
order by effectivedate desc)
Or if you need to see the counts for the 3 dates broken out
SELECT st.effectivedate, count(1)
FROM Person.Contact C
INNER JOIN Sales.SalesPerson SP
ON C.ContactID = SP.SalesPersonID
FULL OUTER JOIN Sales.SalesTerritory ST
ON ST.TerritoryID = SP.TerritoryID
WHERE ST.effctivedate in (select distinct top 3 effectivedate
from salesterritory
order by effectivedate desc)
GROUP BY st.effectivedate
You can also use the analytic RANK function. This query will number the latest date as 1, the next latest as 2, and so forth:
SELECT
ST.EffectiveDate,
ROW_NUMBER() OVER (ORDER BY ST.EffectiveDate DESC) AS DateRank
FROM Person.Contact C
INNER JOIN Sales.SalesPerson SP ON C.ContactID = SP.SalesPersonID
FULL OUTER JOIN Sales.SalesTerritory ST ON ST.TerritoryID = SP.TerritoryID
You can't use the ranked value in the WHERE clause, so you'll need to take the query above and make it a subquery or a common table expression (CTE).
Subquery version:
SELECT EffectiveDate, COUNT(*)
FROM (
SELECT
ST.EffectiveDate,
ROW_NUMBER() OVER (ORDER BY ST.EffectiveDate DESC) AS DateRank
FROM Person.Contact C
INNER JOIN Sales.SalesPerson SP ON C.ContactID = SP.SalesPersonID
FULL OUTER JOIN Sales.SalesTerritory ST ON ST.TerritoryID = SP.TerritoryID
) DateList
WHERE DateRank <= 3
GROUP BY EffectiveDate
CTE version:
WITH DateList AS (
SELECT
ST.EffectiveDate,
ROW_NUMBER() OVER (ORDER BY ST.EffectiveDate DESC) AS DateRank
FROM Person.Contact C
INNER JOIN Sales.SalesPerson SP ON C.ContactID = SP.SalesPersonID
FULL OUTER JOIN Sales.SalesTerritory ST ON ST.TerritoryID = SP.TerritoryID
)
SELECT EffectiveDate, COUNT(*)
FROM DateList
WHERE DateRank <= 3
GROUP BY EffectiveDate
If you're dealing SQL Server 2005 and above, you could even try this:
;with cte as
(
SELECT
ST.EffectiveDate
FROM Person.Contact C
INNER JOIN Sales.SalesPerson SP
ON C.ContactID = SP.SalesPersonID
FULL OUTER JOIN Sales.SalesTerritory ST
ON ST.TerritoryID = SP.TerritoryID
)
Select EffectiveDate, count(1)
from cte
where EffectiveDate in (select distinct top 3 effectivedate
from cte
order by EffectiveDate desc)
group by EffectiveDate
Though untested, it should work; it my be unnecessarily elaborate though.

Can I return multiple unmatched results from a single SQL join without using a cursor?

I have two tables.
Employee
EmployeeID, EmployeeName, DocumentType
and
DocumentType
DocumentTypeID, DocumentType
There are Multiple Employees and Multiple DocumentTypes.
For each employee, I am trying to display which DocumentTypes do not exist.
I cant seem to do this for each EmployeeID/Employee Name.
I can only get a list of DocumentTypes that dont exist for ALL employees.
Can I do this without a cursor going through each EmployeeID?
EDIT Better version
select distinct e.EmployeeID, e.EmployeeName, t.DocumentTypeID, t.DocumentType
from Employee e
cross join DocumentType t
left join Employee e2 on e2.EmployeeID = e.EmployeeID and t.DocumentTypeID = e2.DocumentTypeID
where e2.EmployeeID is null
Original -
This works but doesn't feel like the most elegant solution
select distinct e.EmployeeID, e.EmployeeName, dt.DocumentTypeID, dt.DocumentType
from Employee e
outer apply (
select * from DocumentType t
where not exists (
select 1
from Employee e2
where e2.DocumentTypeID = t.DocumentTypeID
and e2.EmployeeID = e.EmployeeID)
) dt
I guess you are looking for data something like this
EmployeeID EmployeeName DocumentTypeNotMapped
1 abc doctype1, doctype3, doctype4
2 def doctype3, doctype2, doctype7
Using the query
SELECT ET.EmployeeID, ET.EmployeeName,
SELECT LEFT(DocumentType, LEN(DocumentType) - 1)
FROM (
SELECT DISTINCT DocumentType + ', '
FROM DocumentTypeTable
WHERE DocumentType != ET.DocumentType
FOR XML PATH ('')
) D (DocumentType)
FROM EmployeeTable ET
assuming you are using sql server
;with cte as (
select E.EmployeeID, E.DocumentType,D.DocumentTypeID from Employee E
left outer join Document D
on E.DocumentType<>D.DocumentTypeID),
cte1 as (select EmployeeID,DocumentTypeID from cte
group by EmployeeID,DocumentTypeID
having count(*)>1)
select * from cte1
union all
select EmployeeID,DocumentTypeID from cte where EmployeeID not in
(select EmployeeID from cte1)
order by EmployeeID,DocumentTypeID