Write a self join query? - sql

I have the following table with values
CREATE TABLE #tmpEmployee(ID int, EmpName varchar(50), EmpBossID int)
insert into #tmpEmployee values ( 1, 'Abhijit', 2);
insert into #tmpEmployee values ( 2, 'Haris', 3);
insert into #tmpEmployee values ( 3, 'Sanal', 0);
Now I want the result become following
ID EmpName BossName
1 Abhijit Haris
2 Haris Sanal
so I have written the following query.
select E1.ID,E1.EmpName, E.EmpName as BossName from #tmpEmployee E inner join #tmpEmployee E1 on E1.EmpBossID=E.ID.
But the problem is the 3rd employee (Sanal) has no boss.
So I want this exact result:
ID EmpName BossName
1 Abhijit Haris
2 Haris Sanal
3 Sanal Null
What should I do?

Use Right Join
select E1.ID,E1.EmpName, E.EmpName as BossName from #tmpEmployee E right join #tmpEmployee E1 on E1.EmpBossID=E.ID
ID EmpName BossName
1 Abhijit Haris
2 Haris Sanal
3 Sanal NULL
I think its ok for u

Use a LEFT JOIN and reverse the order of your tables:
select
E.ID,
E.EmpName,
B.EmpName as BossName
from tmpEmployee E
left join tmpEmployee B on E.EmpBossID = B.ID
See a live demo of this query on SQLFiddle
Putting the "employee" part of the join first means that all employees are listed.
Using a left join means that employees without a boss (eg the CEO) will still be listed, but will have a null for the BossName column.
If you truly want only employee listed if they have a boss, change the query to simply JOIN instead of LEFT JOIN (note that the default join type is INNER)
p.s. formatting your query doesn't hurt either:

try out this...
Use Left Join..
select E.ID,E.EmpName, E1.EmpName as BossName from #tmpEmployee E left outer join #tmpEmployee E1 on E1.EmpBossID=E.ID
ID EmpName BossName
1 Abhijit Haris
2 Haris Sanal
3 Sanal NULL

Related

Include Groups not having values for count

I have the following query:
SELECT DepartmentID
, Count(EmployeeID) AS CountEmployee
FROM HR.EmployeeTransfer
GROUP BY DepartmentID
This is my current output:
DepartmentID CountEmployee
1 15
2 20
I want to include Departments which don't have any employees count like below:
DepartmentID CountEmployee
1 15
2 20
3 NULL
4 NULL
Presumably, you have a "departments" table of some sort. For this query, you want a LEFT JOIN from this table:
SELECT d.DepartmentID, Count(et.EmployeeID) AS CountEmployee
FROM HR.Departments d LEFT JOIN
HR.EmployeeTransfer et
ON d.DepartmentID = et.DepartmentID
GROUP BY d.DepartmentID;
Note that this returns 0 instead of NULL. If you really want NULL, you can use:
SELECT d.DepartmentID, NULLIF(Count(et.EmployeeID), 0) AS CountEmployee
SELECT ET.DepartmentID
,Count(E.EmployeeID) AS CountEmployee
FROM HR.EmployeeTransfer AS ET
LEFT JOIN EmployeesTable AS E ON E.DepartmentID=ET.DepartmentID
GROUP BY ET.DepartmentID

Inner Join VS CrossApply

I'm trying to get the same result using two different methods but it appears that
I'm doing wrong somewhere and I can't figure it out
first Query with innerjoin:
with cte as(
select *, ROW_NUMBER() OVER(ORDER BY businessentityid desc) AS Row#
from HumanResources.Employee
)
Select A.firstname, B.jobtitle
from Person.Person A
inner join cte B on A.BusinessEntityID = B.BusinessEntityID and B.Row# <= 3
Second Query with Cross Apply:
Select A.firstname, cte.jobtitle
from Person.Person A
cross apply
(
Select top 3 *
from HumanResources.Employee B
where B.BusinessEntityID= A.BusinessEntityID
order by businessentityid DESC
) cte
Am getting two different results, any ideas ??
Update :
Thanks to #PriyankJ who gave me the idea,I managed to find the correct query thus the desired result :
SELECT **top 3** cte.FirstName ,B.JobTitle
FROM **HumanResources.Employee B**
CROSS APPLY
(
SELECT TOP 3 *
FROM **person.person A**
WHERE A.BusinessEntityID = B.BusinessEntityID
ORDER BY
A.BusinessEntityID DESC
) cte
the changes(difference between the first and second query) are highlighted :)
I think the cross apply left table needs to be HumanResources.Employee to get the same result.
SELECT TOP 3
cte.firstname
,A.jobtitle
FROM HumanResources.Employee A
CROSS APPLY (SELECT
*
FROM Person.Person B
WHERE B.BusinessEntityID = A.BusinessEntityID) cte
ORDER BY cte.BusinessEntityID DESC
So you asked 'why.' This may help.
It's important to understand better what JOIN and CROSS APPLY are really doing.
Consider this schema:
DECLARE #Person as Table (BusinessEntityID int, firstname varchar(10))
DECLARE #Employee as Table (BusinessEntityID int, jobtitle varchar(10))
INSERT INTO #Person (BusinessEntityID, firstname) Values
(1,'Annie'), (2,'Brad'), (3,'Coraline'), (4,'David')
INSERT INTO #Employee (BusinessEntityID, jobtitle) Values
(1,'Director'), (2,'Manager'), (3,'Analyst'), (4,'Gopher')
And your first query
with cte as(
select *, ROW_NUMBER() OVER(ORDER BY businessentityid desc) AS Row#
from #Employee
)
Select A.firstname, B.jobtitle
from #Person A
inner join cte B on A.BusinessEntityID = B.BusinessEntityID and B.Row# <= 3
Which returns this:
firstname jobtitle
---------- ----------
Brad Manager
Coraline Analyst
David Gopher
Your CTE is being limited to the top 3 records by BusinessEntityID (descending). So Annie isn't in that set. Then we do inner join, which returns all records from both sets that match on the joined column. So, Director from the Employee table falls out, and our final set is 3 records. But what's the cross apply doing?
Select A.firstname, cte.jobtitle
from #Person A
cross apply
(
Select top 3 *
from #Employee B
where B.BusinessEntityID= A.BusinessEntityID
order by businessentityid DESC
) cte
firstname jobtitle
---------- ----------
Annie Direcor
Brad Manager
Coraline Analyst
David Gopher
This is different. CROSS APPLY doesn't match the two sets on a joined column. Rather, it evaluates the right side for every row on the left side. There's four rows in Person, and for each one, Cross Apply selects the top 3 records from Employee that will match that row on BusinessEntityID (because of the where clause). That's going to give you 4 rows. Let's change our Employee table a little.
INSERT INTO #Employee (BusinessEntityID, jobtitle) Values
(1,'Direcor'),(2,'Manager'),(3,'Analyst'),(4,'Gopher'),(4,'Blacksmith')
We've added a Blacksmith with a duplicate Entity id. What do we get?
JOIN LOGIC
firstname jobtitle
---------- ----------
Coraline Analyst
David Gopher
David Blacksmith
CROSS APPLY LOGIC
firstname jobtitle
---------- ----------
Annie Direcor
Brad Manager
Coraline Analyst
David Gopher
David Blacksmith
We see that in the joined CTE, the duplicated id has pushed one of our smaller id's off the list of top 3 BusinessEntityIds, and David shows up twice where we've joined on the duplicated IDs.
In the cross apply, we have our four rows from Person, and the top three rows from Employee that match each one of those rows from person. In the case of BusinessEntityID = 4, there are two matches and we get both of them.
The most important point is that Cross apply is evaluating the right side in the context of each row on the left. Join just goes and returns the rows that match.
If you want your Cross Join query to imitate the same 'filtering' effect that an inner join can create, then you must put the table that will be limited on the left side of the cross join.
;with cte as(
select *, ROW_NUMBER() OVER(ORDER BY businessentityid desc) AS Row#
from #Employee
)
Select A.firstname, B.jobtitle
from cte B
cross join #Person A
where A.BusinessEntityID = B.BusinessEntityID and B.Row# <= 3
firstname jobtitle
---------- ----------
David Gopher
David Blacksmith
Coraline Analyst
what you need is 'outer apply'. Try this:
Select A.firstname, cte.jobtitle
from Person.Person A
outer apply
(
Select top 3 *
from HumanResources.Employee B
where B.BusinessEntityID= A.BusinessEntityID
order by businessentityid DESC
) cte
Most important thing is why do you want to use CROSS APPLY or INNER JOIN ?
you can try in this manner,
Select A.firstname, cte.jobtitle
from Person.Person A
cross apply
(
Select * from
(
Select top 3 *
from HumanResources.Employee B
order by businessentityid DESC
)B
where B.BusinessEntityID= A.BusinessEntityID
) cte
Now you can easily understand why you were getting different result .

How to get the values from 2 tables together in SQL

I have 2 tables
1. emp_mst
empcode empname
001 abc
002 def
2. leavetotal
empcode leave
001 10
001 5
001 2
002 12
002 8
Now i am trying to get the empcode and empname from the emp_mst
and the total for leave days from leavetotal.I have no idea how to get it.
thanks in advance.
In the FROM clause you can specify multiple tables, this will result in a cartesian product of the two tables. So each row in the a table will be joined with every other row in all other tables. This is of course not what you want, you only want rows with the same empcode to be joined. So that needs to be specified in the WHERE CLAUSE.
SELECT
MST.EMPCODE,
MST.EMPNAME,
SUM(LTO.LEAVE)
FROM
EMP_MST MST,
LEAVETOTAL LTO
WHERE
MST.EMPCODE = LTO.EMPCODE
GROUP BY
EMPCODE,
EMPNAME
JOIN / GROUP BY solution:
select e.empcode, e.empname, SUM(l.leave)
from emp_mst e
left join leavetotal l on e.empcode = l.empcode
group by e.empcode, e.empname
LEFT JOIN to list even those without any leave. (Do just JOIN if not needed.)
Correlated sub-select solution:
select e.empcode, e.empname,
(select SUM(l.leave) from leavetotal l
where e.empcode = l.empcode)
from emp_mst e
You are looking for a join, and then a group by:
SELECT em.empcode, empname, SUM(leave)
FROM emp_mst em
JOIN leavetotal l ON em.empcode = l.empcode
GROUP BY em.empcode, empname

How to return hierarchical data?

From employee table below, I need to retrieve an employee and the manager. Can someone help me? Thanks in advance.
tblEmployee:
empId empName manID
1 A
2 B 1
3 C 2
Output:
Name Manager
A -
B A
C B
A pair of rudimentary approaches would be
SELECT
'Name' = empName
, 'Manager' = (SELECT empName FROM tblEmployee WHERE empID = e.manID)
FROM tblEmployee e
or
SELECT
'Name' = e.empName
, 'Manager' = ISNULL(m.empName, 'none')
FROM tblEmployee e
LEFT JOIN tblEmployee m
ON e.manID = m.empID
There is probably a better approach depending on the DBMS you are using.
You simple need to join the table with itself:
For mysql it would be :
SELECT
t1.empName AS 'Name',
IFNULL(t2.empName, '-') AS 'Manager'
FROM
myTable t1
LEFT JOIN
myTable t2
ON
t1.manID = t2.empId
t1 now would be the base-user, while t2 would contain the manager, if present. If there is no manager, t2.empName will be null and then be replaced with a - by the ifnull function.
Make use of Right and self joins
select t2.empName, t1.empName as Manager from tblEmployee t1
right Join tblEmployee t2 on t1.empID = t2.manID
order by empName

SQL JOIN : Help using join inisted subquery

I have 2 tables,
The first one is called emp, and has 2 columns called id and name
The second one is called dep, and has columns called id and empid and nameOfDep
if I want to list all emp that have X dep, but don't have Y dep
This is an example I use
Select e.id, e.name
from emp e
where e.id in (Select empid from deptid where deptid=X)
and e.id not in (Select empid from deptid where deptid=Y);
How I can make it using JOIN instead of with subqueries?
An IN can be converted into an INNER JOIN. A Not IN can be converted to LEFT JOIN / NULL Test. Sometimes called an ANTI JOIN.
SELECT e.id,
e.name
FROM emp e
INNER JOIN deptid D_X
ON e.empid = d_x.empid
AND deptid = 'X'
LEFT JOIN deptid D_Y
ON e.empid = d_Y.empid
AND deptid = 'Y'
WHERE d_Y.empid IS NULL
Also I'm making the assumption that when you wrote deptid = X that you meant X to be a literal string and not a field name
SELECT e.id, e.name
FROM emp e
INNER JOIN dep d ON (e.deptID = d.deptID AND d.deptID NOT y)
Add the Department ID to the employee record and then join on that.
EDIT
My bad, updated.
EDIT
Helps to read, go with Conrad's answer.