self join query - sql

emp_id
emp
level
manager_id
manager
100
sam
1
100
sam
200
jack
2
100
sam
300
jill
2
100
sam
400
claire
3
200
jack
500
reed
3
300
jill
600
derrick
4
400
claire
700
bill
4
500
reed
I have a table with employees and their respective managers in column 'emp' and 'manager' respectively. The numbers in the 'level' column represent different levels within an organisation for the employees in column 'emp'.
How do i write a query to get the manager name from the previous level as an entry in step and so forth.
emp_id
emp
level
manager_id
manager
l1
l2
l3
l4
100
sam
1
100
sam
NULL
NULL
NULL
NULL
200
jack
2
100
sam
sam
NULL
NULL
NULL
300
jill
2
100
sam
sam
NULL
NULL
NULL
400
claire
3
200
jack
sam
jack
NULL
NULL
500
reed
3
300
jill
sam
jill
NULL
NULL
600
derrick
4
400
claire
sam
jack
claire
NULL
700
bill
4
500
reed
sam
jill
reed
NULL

You seem to be looking for a recursive query. One that keeps joining on the next level until there are no more levels to join to.
That can be used to get all of an employees managers, each manager as a new row.
You then want to pivot those rows in to columns. Note, however, that SQL is statically and strongly typed, which means that if you want a pivoted view, you have to choose in advance how many columns you're going to have.
For example...
WITH
recurse_upwards AS
(
SELECT
emp.emp_id,
emp.emp AS emp_name,
0 AS level,
mgr.emp_id AS manager_id,
mgr.emp AS manager_name,
mgr.manager_id AS next_manager_id
FROM
example AS emp
LEFT JOIN
example AS mgr
ON mgr.emp_id = emp.manager_id
UNION ALL
SELECT
emp.emp_id,
emp.emp_name,
emp.level + 1,
mgr.emp_id,
mgr.emp,
mgr.manager_id
FROM
recurse_upwards AS emp
INNER JOIN
example AS mgr
ON mgr.emp_id = emp.next_manager_id
)
SELECT
emp_id,
emp_name,
MAX(CASE WHEN level = 0 THEN manager_id END) AS manager_id,
MAX(CASE WHEN level = 0 THEN manager_name END) AS manager_name,
MAX(CASE WHEN level = 1 THEN manager_name END) AS manager_1_name,
MAX(CASE WHEN level = 2 THEN manager_name END) AS manager_2_name,
MAX(CASE WHEN level = 3 THEN manager_name END) AS manager_3_name,
MAX(CASE WHEN level = 4 THEN manager_name END) AS manager_4_name
FROM
recurse_upwards
GROUP BY
emp_id,
emp_name
ORDER BY
emp_id
Demo : https://dbfiddle.uk/Tj7rZ5bT

Related

List the emps whose sal > his Manager but less than other Managers

List the emps whose sal > his Manager but salary less than other Managers
Table
emp_ID emp_Name emp_sal_K emp_manager
1 Ali 200 3
2 Zaid 620 4
3 Mohd 1140 2
4 LILY 600 NULL
5 John 1240 6
6 Mike 1160 4
7 John 1240 6
8 Mohd 1640 2
Query
select *
from emp_demo2 e1
where emp_sal_K >
(select emp_ID from emp_demo2 e2
where e1.emp_manager = e2.emp_ID and e2.emp_sal_K
< all
(select emp_id from emp_demo2 e3
where e2.emp_ID = e3.emp_id))
Result: null columns. There are 4 managers in total 2,4 ,6 and 3. Here clearly emp_id 2 Zaid is a person whose salary 620 is greater than his manager's salary emp_id 4 which is 600 but less than all other managers emp_id 6 and 3. So I should get that result but getting nothing.
Expected result
emp_ID emp_Name emp_sal_K emp_manager
2 Zaid 620 4
SELECT emp.*
FROM emp_demo2 emp
LEFT JOIN emp_demo2 mgr
ON emp.emp_manager = mgr.emp_id
WHERE emp.emp_sal_k > mgr.emp_sal_k
AND emp.emp_sal_k < (SELECT Min(mgr1.emp_sal_k)
FROM emp_demo2 emp1
JOIN emp_demo2 mgr1
ON emp1.emp_manager = mgr1.emp_id
WHERE mgr1.emp_id <> emp.emp_manager
AND mgr1.emp_id <> emp.emp_id)

SQL query to join two tables without common column in oracle DB

I have 2 tables employee and job_role as below. I need to write a SQL query to find designation of each employee by joining this table.
Input Table
Employee
e_id e_name Salary
-----------------------
1 ABC 1000
2 CDE 2000
3 GHI 3500
4 JKL 5000
5 MNO 4000
6 XYZ 3000
Job_role
Designation Sal_min Sal_max
-------------------------------
Associate 1000 2000
Lead 2001 3000
Manager 3001 5000
Problem: if salary from employee table is in the range sal_min to sal_max, find the designation
Desired output:
e_id e_name Salary Designation
-----------------------------------
1 ABC 1000 Associate
2 CDE 2000 Associate
3 GHI 3500 Manager
4 JKL 5000 Manager
5 MNO 4000 Manager
6 XYZ 3000 Lead
The ON clause can consist of other operations than =. Here you can use <= and >= (or BETWEEN if you like).
SELECT E.E_ID,
E.E_NAME,
JR.DESIGNATION
FROM EMPLOYEE E
LEFT JOIN JOB_ROLE JR
ON JR.SAL_MIN <= E.SALARY
AND JR.SAL_MAX >= E.SALARY;
(Note: I used LEFT JOIN for the case that there are any employees where the salary doesn't match. I guessed you rather want to see them with a NULL designation than not at all.)

How to increase the salary of an employee based on the number of children in SQL ORACLE 10G?

I have two tables: employee and dependent
Structure
- Table employee
Name Null? Type
EMPLOYEEID NOT NULL NUMBER(3)
LNAME NOT NULL VARCHAR2(15)
FNAME NOT NULL VARCHAR2(15)
POSITIONID NUMBER(1)
SUPERVISOR NUMBER(3)
HIREDATE DATE
SALARY NUMBER(6)
COMMISSION NUMBER(5)
DEPTID NUMBER(2)
QUALID NUMBER(1)
- Table Dependent
Name Null? Type
EMPLOYEEID NOT NULL NUMBER(3)
DEPENDENTID NOT NULL NUMBER(1)
DEPDOB NOT NULL DATE
RELATION NOT NULL VARCHAR2(8)
Data
- Table employee
EMPLOYEEID LNAME FNAME POSITIONID SUPERVISOR HIREDATE SALARY COMMISSION DEPTID QUALID
111 Smith John 1 15/04/60 265000 35000 10 1
246 Houston Larry 2 111 19/05/67 150000 10000 40 2
123 Roberts Sandi 2 111 02/12/91 75000 10 2
543 Dev Derek 2 111 15/03/95 80000 20000 20 1
433 McCall Alex 3 543 10/05/97 66500 20 4
135 Garner Stanley 2 111 29/02/96 45000 5000 30 5
200 Shaw Jinku 5 135 03/01/00 24500 3000 30
222 Chen Sunny 4 123 15/08/99 35000 10 3
- Table Dependent
EMPLOYEEID DEPENDENTID DEPDOB RELATION
543 1 28/09/58 Spouse
543 2 14/10/88 Son
200 1 10/06/76 Spouse
222 1 04/02/75 Spouse
222 2 23/08/97 Son
222 3 10/07/99 Daughter
111 1 12/12/45 Spouse
And I have two employees: One employee has one child, and the other has two children. I got it with the following query:
Query
SELECT employee.employeid, lname, fname, salary, dependent.relation
FROM employee INNER JOIN dependent
ON employee.employeeid = dependent.employeeid
WHERE dependent.relation = 'Daughter' OR dependent.relation = 'Son';
Result
EMPLOYEEID LNAME FNAME SALARY RELATION
543 Dev Derek 80000 Son
222 Chen Sunny 35000 Son
222 Chen Sunny 35000 Daughter
And my homework is increase the salary $ 100 per child, and I tried with the following code:
UPDATE employee
SET SALARY = salary + 100
WHERE employee.employeeid IN (
SELECT employee.EMPLOYEEID FROM employee INNER JOIN dependent
ON employee.employeeid = dependent.employeeid
WHERE dependent.relation = 'Daughter' OR dependent.relation = 'Son' );
But, i donĀ“t get the expect result. The employee "Chen" has two children, but his salary only increase once, no twice. Her final salary is $ 35100, no $ 35200.
Can anybody help me?
I can't test on Oracle version 10, but this should work fine:
Well, apparently, it doesn't work in Oracle 10 (though it does work correctly in Oracle 12). I added an alternative at the bottom of the post using merge that should work correctly.
update (
select e.salary,
d.childcnt * 100 as increase
from employee e
join (select d.employeeid, count(*) as childcnt
from dependent d
where d.relation in ('Son', 'Daughter')
group by d.employeeid) d
on d.employeeid = e.employeeid
) set salary = salary + increase
Here is another way of achieving the same thing using the merge command. This should work correctly in Oracle 10:
merge into employee dest
using (
select employeeid,
count(*) * 100 as increase
from dependent d
where relation in ('Son', 'Daughter')
group by employeeid
) src
on (src.employeeid = dest.employeeid)
when matched then
update set dest.salary = dest.salary + src.increase

get extra rows for each group where date doesn't exist

I've been playing with this for days, and can't seem to come up with something. I have this query:
select
v.emp_name as Name
,MONTH(v.YearMonth) as m
,v.SalesTotal as Amount
from SalesTotals
Which gives me these results:
Name m Amount
Smith 1 123.50
Smith 2 40.21
Smith 3 444.21
Smith 4 23.21
Jones 1 121.00
Jones 2 499.00
Jones 3 23.23
Jones 4 41.82
etc....
What I need to do is use a JOIN or something, so that I get a NULL value for each month (1-12), for each name:
Name m Amount
Smith 1 123.50
Smith 2 40.21
Smith 3 444.21
Smith 4 23.21
Smith 5 NULL
Smith 6 NULL
Smith ... NULL
Smith 12 NULL
Jones 1 121.00
Jones 2 499.00
Jones 3 23.23
Jones 4 41.82
Jones 5 NULL
Jones ... NULL
Jones 12 NULL
etc....
I have a "Numbers" table, and have tried doing:
select
v.emp_name as Name
,MONTH(v.YearMonth) as m
,v.SalesTotal as Amount
from SalesTotals
FULL JOIN Number n on n.Number = MONTH(v.YearMonth) and n in(1,2,3,4,5,6,7,8,9,10,11,12)
But that only gives me 6 additional NULL rows, where what I want is actually 6 NULL rows for each group of names. I've tried using Group By, but not sure how to use it in a JOIN statement like that, and not even sure if that's the correct route to take.
Any advice or direction is much appreciated!
Here's one way to do it:
select
s.emp_name as Name
,s.Number as m
,st.salestotal as Amount
from (
select distinct emp_name, number
from salestotals, numbers
where number between 1 and 12) s left join salestotals st on
s.emp_name = st.emp_name and s.number = month(st.yearmonth)
Condensed SQL Fiddle
You could do:
SELECT EN.emp_name Name,
N.Number M,
ST.SalesTotal Amount
FROM ( SELECT Number
FROM NumberTable
WHERE Number BETWEEN 1 AND 12) N
CROSS JOIN (SELECT DISTINCT emp_name
FROM SalesTotals) EN
LEFT JOIN SalesTotals ST
ON N.Number = MONTH(ST.YearMonth)
AND EN.emp_name = ST.emp_name

SQL - how to get certain column with MIN and MAX id for every department?

I'm trying to select some information using SQL, but with no success. Here's what I'm trying to do.
I have 2 tables:
Table employees with following columns:
IDemployee | name | surname | department_id
1 | John | Smith | 1
2 | Jane | Smith | 1
3 | Neo | Anderson | 1
4 | John | Mason | 2
5 | James | Cameron | 2
6 | Morpheus| Grumpy | 2
Table departments with columns:
IDdepartment | name
1 | Thieves
2 | Madmen
I want to select surnames of first and last employees of every department and count of their employees.
Result:
department_name | first_employee | last_employee | employee_count
Thieves | Smith | Anderson | 3
Madmen | Mason | Grumpy | 3
I was able to get count and ID's of first and last employees with following query:
SELECT d.IDdepartment, COUNT(*) as "employee_count", MIN(e.IDemployee) as "first_employee", MAX(e.IDemployee) as "last_employee"
FROM ( employees e INNER JOIN departments d ON d.IDdepartment=e.department_id)
GROUP BY d.name;
However, I can't find the right way to select their surnames. Any help would be greatly appreciated.
While there might be another way, one way is to use your query as a subquery:
SELECT d.name department_name,
e.surname first_employee,
e2.surname last_employee,
t.employee_count
FROM (
SELECT d.IDdepartment,
COUNT(*) as "employee_count",
MIN(e.IDemployee) as "first_employee",
MAX(e.IDemployee) as "last_employee"
FROM employees e
INNER JOIN departments d
ON d.IDdepartment=e.department_id
GROUP BY d.name
) t JOIN employees e on t.first_employee = e.IDemployee
JOIN employees e2 on t.last_employee = e2.IDemployee
JOIN departments d on t.IDdepartment = d.IDdepartment
And here is the fiddle: http://sqlfiddle.com/#!2/17a5b/2
Good luck.
This is general Oracle example based on existing Oracle table. You need to use analytic functions if available in your version of SQL. You do not specify which SQL you are using. If FIRST() and LAST() analytic f-ns available in your SQL then this should work:
SELECT empno, deptno, sal,
MIN(sal) KEEP (DENSE_RANK FIRST ORDER BY sal) OVER (PARTITION BY deptno) "Lowest",
MAX(sal) KEEP (DENSE_RANK LAST ORDER BY sal) OVER (PARTITION BY deptno) "Highest"
FROM scott.emp
ORDER BY deptno, sal
/
See lowest and highest salary by dept in output of above query:
DEPTNO SAL Lowest Highest
---------------------------------
10 1300 1300 5000
10 2450 1300 5000
10 5000 1300 5000
20 800 800 3000
20 1100 800 3000
20 2975 800 3000
....