How to select rows with related values? - sql

I have a table "company" with data like follows containing employees and their companies:
EmployeeID EmployeeName CompanyName CompanyID ParentCoID
------------------------------------------------------------------------------------------------
100 John A 500 NULL
100 John A.1 600 500
250 Paul B 800 NULL
250 Paul ABC 2000 NULL
350 Joe D 1000 5000
600 Tom E 700 NULL
600 Tom E.2 1500 700
I am trying to display rows where if the Parent Co ID value is in the Company ID, then show both rows for that employee. So basically select employees that are in both the parent company and the sub company and show both of those rows.
I've tried
SELECT *
FROM company
WHERE CompanyID IN (SELECT ParentCoID FROM company)
but this only returns the row where the CompanyID value matches and I'm not sure how to also include that second row also.
My desired output for the sample above would be:
EmployeeID EmployeeName CompanyName CompanyID ParentCoID
------------------------------------------------------------------------------------------------
100 John A 500 NULL
100 John A.1 600 500
600 Tom E 700 NULL
600 Tom E.2 1500 700
As from the result above, Company A.1 is a sub company of A, and same with company E and E.2. I am trying to select employees that are in both the main company and sub company and therefore need to refer to the ParentCoID and the CompanyID columns.

SELECT * FROM company
WHERE EmployeeID IN (SELECT EmployeeID FROM company WHERE CompanyID IN (SELECT ParentCoID FROM company)

Please try this:
Updated the query.
SELECT * FROM company c
WHERE CompanyID IN (SELECT ParentCoID FROM company c2 WHERE c2.EmployeeID = c.EmployeeID) OR ParentCoID IN (SELECT CompanyID FROM company c3 WHERE c3.EmployeeID = c.EmployeeID)

Using EXISTS
SELECT a.*
FROM company a
WHERE EXISTS (SELECT b.parentcoid
FROM company b
WHERE a.companyid = b.parentcoid
AND a.employeeid = b.employeeid)
UNION ALL
SELECT c.*
FROM company c
WHERE EXISTS (SELECT d.companyid
FROM company d
WHERE d.companyid = c.parentcoid
AND d.employeeid = c.employeeid)

A recursive CTE is useful in querying hierarchical data.
with cte As (
select * from company c
Where ParentCoID is null and CompanyID = Any(Select ParentCoID From company Where EmployeeID=c.EmployeeID)
union all
select c.*
from cte p inner join company c On (p.CompanyID=c.ParentCoID And p.EmployeeID=c.EmployeeID)
)
select * From cte order by EmployeeID, CompanyID, ParentCoID

Here's a slightly different approach using a simple window function and a self-join and will likely be performant given a clustered index on (EmployeeId,ParentCoId).
with e as (
select EmployeeID,ParentCoID CoId,
Sum(case when ParentCoID is null then 1 end ) over(partition by EmployeeID) IsSub
from Company
)
select c.*
from e
join Company c on c.EmployeeID=e.EmployeeID
where (e.CoId=c.CompanyID or e.CoId=c.ParentCoID)
and e.CoId is not null and e.IsSub=1

Related

SQL Query: Customer Table Contains Different type of EmpIDs and i want to get their name in a single row

I got a customer table and each customer has 4 different employeeID (tranieeID,RepresenterID,CoridatorID and ManagerID) I want to get all these employees name rather then their ID in single row.
CustomerTable
|CustomerID|CustomerName|tranieeID|RepresenterID |CoridatorID |ManagerID
------------------------------------------------------------------------
01 Mr T 100 101 102 103
EmployeeTable
EmpID | EmpName
---------------
100 Mr A
101 Mr B
102 Mr C
103 Mr D
What I need
CustomerID | CustomerName | tranieeName | RepresenterName | CoridatorName | ManagerName
----------------------------------------------------------------------------------------
01 Mr T Mr A Mr B Mr C Mr D
I did inner join but I got 4 Rows, is there any way to get all these with a single row?
Thank you for your help!
JOIN should work. I would recommend LEFT JOIN in case any of the values are not filled in:
select c.*,
et.empname as traineeName,
er.empname as RepresenterName,
ec.empname as CoridatorIDName,
em.empname as ManagerName
from customertable c left join
employeetable et
on c.traineeID = et.empid left join
employeetable er
on c.RepresenterID = et.empid left join
employeetable ec
on c.CoridatorID = ec.empid left join
employeetable em
on c.ManagerID = em.empid
You could use a correlated subquery in each instance, such as
select c.CustomerId, CustomerName,
(select Empname from EmployeeTable e where e.EmpID=c.TranieeId) TraineeName,
(select Empname from EmployeeTable e where e.EmpID=c.RepresenterId) RepresenterName,
... etc
from CustomerTable c

How do I find the highest salary from each department using SUBQUERIES

I'm really new to this and this particular question has been bugging me for days. I do know there are similar questions to this but I kept wondering how it would be done in subqueries.
SALARY TABLE
[Emp_ID] [SalaryPM]
001 | 10,500
002 | 50,000
003 | 8,000
004 | 10,000
DEPT TABLE
[Emp_ID] [Dept_ID]
001 | A
002 | B
003 | C
004 | C
I want it to look like this
[Emp_ID] [Dept_ID] [SalaryPM]
001 | A | 10,000
002 | B | 50,000
004 | C | 10,000
What I have tried so far, but it only gives the highest salary of the employee##
SELECT * FROM DEPT
WHERE EMP_ID IN
(SELECT Emp_ID
FROM SALARY
WHERE SalaryPM = (SELECT MAX(SalaryPM)
FROM SALARY));
Would this qualify as a subquery solution?
select *
from (
select s.*, e.deptid,
rank() over(partition by e.dept order by s.salaries desc) rn
from employees e
inner join salaries s on s.id = e.id
) rn
where rn = 1
Note: your does not look good. The data you are showing suggests a 1-1 relationship between the two tables (wich I called employees and salaries): if so, both tables should be combined in a single table.
I would be good to know what do you mean by "using subqueries" ;)
here's another solution using subquery in the SELECT clause
SELECT
d.id,
d.deptid,
(
SELECT
MAX(s.salary)
FROM
my_salaries s
WHERE
s.id = d.id
) max_salary
FROM
my_departments d;
here's solution without joining tables (IMHO not joining tables is just overcomplicating things). Why 'not using join' is so important for you?
SELECT
sub.id,
MAX(sub.deptid),
MAX(sub.salary)
FROM
(
SELECT
d.id,
d.deptid,
NULL salary
FROM
my_departments d
UNION ALL
SELECT
s.id,
NULL deptid,
s.salary
FROM
my_salaries s
) sub
GROUP BY
sub.id
ORDER BY
sub.id;

parent details in same row conditionally

I have a table Called EmployeeLevel
Id Position ShouldApproveLeave
1000 1 0
2000 2 1
3000 3 0
4000 4 0
Note that the bool column ShouldApproveLeave is true for only one row
I have a table Employee with the following structure with LevelId as foreign key to EmployeeLevel
Id ParentId Name LevelId
1000 NULL Jack 1000
2000 1000 John 2000
3000 2000 Nick 3000
4000 3000 James 4000
I need a query to get employee details, position details as well as the manager responsible for his leave approval as below
query (id = 4000)
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
4000 James 4 2 John
query (id = 3000)
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
3000 Nick 3 2 John
query (id = 2000)
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
2000 John 2 2 John
Also I want to check leave approving manager only upwards in the hierarchy.
query (id = 1000)
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
1000 Jack 1 2 NULL
I wrote a CTE to get the manager with LeaveApproving bit set. Getting the employee with level is also straightforward. My issue is to get all these details in the same row.
So the question really is I got individual results for employee details and Leave Approving manager. How can I get this in a single row?
I managed to get individual queries for the following
employee details
Id ParentId Name Position
4000 3000 James 4
LeaveApprovalLevel details
Level
2
LeaveApprovingManager details
Id ParentId Name
2000 1000 John
How can I join these so that I will get this?
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
4000 James 4 2 John
Any tips are appreciated.
Here is one approach:
Get the hierarchy for the employee.
Use joins to get the information on the flag you want.
Use joins to bring back all the information you want.
As a query:
with cte as (
select e.id, e.parentid, 1 as lev
from employee e
where id = 4000
union all
select cte.id, e.parentid, lev + 1
from cte join
employee e
on e.id = cte.parentid
)
select e.*, el.*
from cte join
employee e
on cte.id = e.id join
employee el
on cte.parentid = el.id join
EmployeeLevel eml
on cte.parentid = eml.id;
try this
SELECT e1.Id, e1.Name, el1.Position as LevelPosition, el2.Position as LeaveApprovalLevel, e2.Name as LeaveApprovingManager FROM Employee e1 INNER JOIN EmployeeLevel el1 ON e1.LevelId = el1.Id LEFT JOIN Employee e2 ON e1.ParentId = e2.Id LEFT JOIN EmployeeLevel el2 ON e2.LevelId = el2.Id;
Here is the response
Check, here is the working Fiddle

Select records that appear more than once

I am trying to select records that appear more than once and are part of a specific department plus other departments.
So far the query that I have is this:
SELECT employeeCode, employeeName
FROM
Employees
WHERE
Department <> 'Technology'
AND employeeCode IN (SELECT employeeCode
FROM Employees
GROUP BY employeeCode HAVING COUNT(*) > 1)
The problem is that I want to select employees which are part of the Technology department, but they also participate in other departments.
So, they must be from the Technology department, but they could also be from the Household department. In the database it could look like:
1 | A1 | Alex | Technology
2 | A2 | Thor | Household
3 | A3 | John | Cars
4 | A3 | John | Technology
5 | A4 | Kim | Technology
6 | A4 | Kim | Video Games
So basically the query should return:
A3 | John |
A4 | Kim |
I think it's a small part that I am missing but..
Any ideas on how to filter/sort it so that it always uses the technology and the other departments?
Btw, I tried searching but I couldn't find a problem like mine..
If you want employees that could be in the technology department and another department:
select e.employeeCode, e.employeeName
from employees e
group by e.employeeCode, e.employeeName
having sum(case when e.department = 'Technology' then 1 else 0 end) > 0 and
count(*) > 1;
This assumes no duplicates in the table. If it can have duplicates, then use count(distinct department) > 1 rather than count(*) > 1.
Try this:
SELECT E.employeeCode, E.employeeName
FROM Employees E
INNER JOIN (SELECT DISTINCT E1.employeeCode, E1.employeeName
FROM Employees E
WHERE E.Department = 'Technology'
) AS A ON E.employeeCode = A.employeeCode AND E.employeeName = A.employeeName
GROUP BY E.employeeCode, E.employeeName
HAVING COUNT(*) > 1;
You can use EXISTS with correlated sub-query joining on the same table with different condition.
SELECT e1.employeeCode, e1.employeeName
FROM Employees e1
WHERE e1.Department = 'Technology'
AND EXISTS (SELECT * FROM Employees e2
WHERE e1.employeeCode = e2.employeeCode
AND e2.Department <> 'Technology')
This will work for your case:
SELECT a.employeeCode, a.employeeName
FROM Employees a, Employees b
WHERE
a.Department = 'Technology'
AND
b.Department <> 'Technology'
AND
a.employeeCode = b.employeeCode
AND
a.employeeID <> b.employeeID

select dept names who have more than 2 employees whose salary is greater than 1000

How would do the following in SQL
"select dept names who have more than 2 employees whose salary is greater than 1000" ?
DeptId DeptName
------ --------
1 one
2 two
3 three
EmpId DeptId Salary
----- ------ ------
121 1 2000
122 1 2000
123 1 5000
124 1 4000
131 2 2000
132 2 6000
133 2 1000
134 2 1000
125 3 1000
126 3 20000
RESULT: one
How about something like this?
SELECT D.DeptName FROM
Department D WHERE (SELECT COUNT(*)
FROM Employee E
WHERE E.DeptID = D.DeptID AND
E.Salary > 1000) > 2
SELECT DEPTNAME
FROM(SELECT D.DEPTNAME,COUNT(EMPID) AS TOTEMP
FROM DEPT AS D,EMPLOYEE AS E
WHERE D.DEPTID=E.DEPTID AND SALARY>1000
GROUP BY D.DEPTID
)
WHERE TOTEMP>2;
select min(DEPARTMENT.DeptName) as deptname
from DEPARTMENT
inner join employee on
DEPARTMENT.DeptId = employee.DeptId
where Salary > 1000
group by (EmpId) having count(EmpId) > =2
hope this helps
select DeptName from DEPARTMENT inner join EMPLOYEE using (DeptId) where Salary>1000 group by DeptName having count(*)>2
select D.DeptName from [Department] D where D.DeptID in
(
select E.DeptId from [Employee] E
where E.Salary > 1000
group by E.DeptId
having count(*) > 2
)
select deptname from dept_1
where exists
(
SELECT DeptId,COUNT(*)
FROM emp_1
where salary>1000
and emp_1.deptid=dept_1.deptid
GROUP BY DeptId
having count(*)>2)
1:list name of all employee who earn more than RS.100000 in a year.
2:give the name of employee who earn heads the department where employee with employee I.D
My main advice would be to steer clear of the HAVING clause (see below):
WITH HighEarners AS
( SELECT EmpId, DeptId
FROM EMPLOYEE
WHERE Salary > 1000 ),
DeptmentHighEarnerTallies AS
( SELECT DeptId, COUNT(*) AS HighEarnerTally
FROM HighEarners
GROUP
BY DeptId )
SELECT DeptName
FROM DEPARTMENT NATURAL JOIN DeptmentHighEarnerTallies
WHERE HighEarnerTally > 2;
The very early SQL implementations lacked derived tables and HAVING was a workaround for one of its most obvious drawbacks (how to select on the result of a set function from the SELECT clause). Once derived tables had become a thing, the need for HAVING went away. Sadly, HAVING itself didn't go away (and never will) because nothing is ever removed from standard SQL. There is no need to learn HAVING and I encourage fledgling coders to avoid using this historical hangover.