how to navigate in self loop tables? - sql

Consider the following table
create table EMPLOYEE
(
empno NUMBER not null,
ename VARCHAR2(100),
salary NUMBER,
hiredate DATE,
manager NUMBER
);
alter table EMPLOYEE add constraint PK_EMP primary key (EMPNO);
alter table EMPLOYEE
add constraint FK_MGR foreign key (MANAGER)
references EMPLOYEE (EMPNO);
which is a self looped table i.e. every employee has a manager, except for the root.
I want to run the following query on this table:
find all the employees having more salary than their managers?
P.S.
There is only one root in the structure
consider the following query
SELECT LPAD(emp.ename, (LEVEL-1)*5 + LENGTH(emp.ename), ' ') AS "Hierarchy"
FROM employee emp
START WITH emp.manager IS NULL
CONNECT BY manager = PRIOR empno;
the result would be something like this:
Alice
Alex
Abbey
Sarah
Jack
Bill
Jacob
Valencia
Bob
Babak
...
I made the following query
SELECT LPAD(emp.ename, (LEVEL-1)*5 + LENGTH(emp.ename), ' ') AS "Hierarchy"
FROM employee emp
START WITH empno IN (SELECT empno FROM employee)
CONNECT BY PRIOR manager = empno;
which makes a subtree for every employee in the employee table from bottom to top, but I don't know how to navigate through to get to the desired result!

Here is one way to do it
with fullemployee (empno, ename, salary, key)
as
(
select A.empno, A.ename, A.salary, A.empno || '.' from
employee A
where A.manager is null
union all
select C.empno, C.ename, C.salary, D.key || '.' || C.empno from
employee C
inner join fullemployee D on C.manager = D.empno
)
select E.ename, F.ename as manager from fullemployee E
inner join fullemployee F on E.key like F.key || '%' and E.key <> F.key
where E.salary > F.salary
or equivalently
with fullemployee (empno, ename, salary, key)
as
(
SELECT empno, ename, salary, SYS_CONNECT_BY_PATH(empno, '.') || '.'
FROM employee
START WITH manager is null
CONNECT BY PRIOR empno = manager
)
select E.ename, F.ename as manager from fullemployee E
inner join fullemployee F on E.key like F.key || '%' and E.key <> F.key
where E.salary > F.salary
SQL Fiddle - http://sqlfiddle.com/#!4/37f4ae/35

This should do the work. Remove that or condition if you don't want the 'root' in your list.
select e.empno, e.ename, e.salary from employee e
inner join employee mgr on mgr.empno = e.manager
where e.salary > mgr.salary
or (e.manager = mgr.empno)

Related

Manager name starting with letter

In a table with columns EMP_ID, EMP_NAME & MANAGER_ID fetch manager names for all employees whose names start with 'A'. Assume a manager is also an Employee.
Table: EMP
EMP_ID
EMP_NAME
MGR_ID
Let's assume you don't have a manager's table, all you have is this employee table. Then,
SELECT EMP_NAME
FROM EMP
WHERE EMP_ID IN (
SELECT MGR_ID
FROM EMP
WHERE EMP_NAME LIKE "A%"
)
should do the task.
You simply need a self join -
SELECT E1.EMP_NAME EMPLOYEE_NAME, E2.EMP_NAME MANAGER_NAME
FROM EMP E1
LEFT JOIN EMP E2 ON E1.MGR_ID = E2.EMP_ID
WHERE E1.EMP_NAME LIKE 'A%';

I need to display all employees which were hired before their managers. Can somebody help me?

I need to run an SQL query which will display all the employees who were hired before their managers and they have the minimum salary for his function.
SELECT ENAME,JOB, SALAR FROM EMP
WHERE JOB = 'SALESMAN'
OR JOB = 'CLERK'
OR JOB = 'ANALYST'
Use window functions:
select . . .
from (select e.*, m.hiredate as mgr_hiredate,
min(salar) over (partition by job) as min_salar
from emp e left join
emp em
on e.mgr = m.empno
) em
where hiredate < mgr_hiredate and salar = min_salar;
You can join EMP to itself to add details about each employee's manager (we're interested in his HIREDATE). Later in WHERE section you can check that manager's HIREDATE is later than corresponding employee's.
You can also pre-calculate all minimal salaries for each job, then join this information to each employee and later you can check that employee's salary is equal to the minimal.
SELECT EMPNO, ENAME
FROM EMP e
INNER JOIN EMP m ON e.MGR = m.EMPNO
INNER JOIN (
SELECT JOB, MIN(SALAR) as MINSALAR
FROM EMP
GROUP BY JOB
) s ON s.JOB = e.JOB
WHERE m.HIREDATE > e.HIREDATE AND e.SALAR = s.MINSALAR

small tricky query on self join

I have a table EMP with columns as below:
create table emp(
empno number(4,0),
ename varchar2(10),
job varchar2(9),
mgr_id number(4,0),
sal number(7,2),
deptno number(2,0));
I want to list all employees' names along with their manager names, including those who do not have a manager. For those employees, their manager's name should be displayed as 'BOSS'.
The following query should work:
select e.ename, (case when m.ename is null then 'BOSS' else m.ename end) as mgrName
from emp e
left join emp m on m.empno = e.mgr_id
To my mind, the better solution is proposed by Charanjith.
In Oracle, we could even use NVL function instead of "case when" in order to replace null value by something. The result should be the same.
select e.ename empName, NVL(m.ename, 'BOSS') mgrName from emp e
left join emp m on m.empno = e.mgr_id
Moreover, we could see another solution : using inner join to filter on emp when a manager exists. Then union for all employees who don't have any manager.
select e.ename empName, m.ename mgrName from emp e inner join emp m on e.mgr_id = m.empno
union
select e.ename empName, 'BOSS' mgrName from emp e where not exists (select 1 from emp m where e.mgr_id = m.empno)
This work fine in oracle:
SELECT e.ename,
nvl(m.ename, 'BOSS')mgr
FROM emp a
LEFT JOIN emp b
ON m.empno = e.mgr_id;

SQL - Partition by - from Oracle to SQL server

I have two tables as below:
create table emp
( empno NUMBER(4) constraint E_PK primary key
, job VARCHAR2(8)
, deptno NUMBER(2) default 10
) ;
create table departments
( deptno NUMBER(2) constraint D_PK primary key
, dname VARCHAR2(10)
) ;
In Oracle, I have a query to show all employee with these position title of each department
select d.dname as department
, e.job as job
, e.ename as emp
from emp e
PARTITION BY (JOB)
right outer join
departments d
using (deptno)
order by department, job;
And the result:
DEPARTMENT JOB EMP
---------- -------- --------
ACCOUNTING Designer Chris
Peter
Manager Mike
Tester null
TRAINER null
HR Designer
Manager
Tester
TRAINER
SALES Designer Black
Manager Jane
Tester Mary
Jack
Wil
Take
TRAINER null
TRAINING Designer Jane
Manager null
Tester null
TRAINER Fake
Smart
Tom
Ana
Can I convert this query to SQL Server (2012 version)
Thanks
You can join the 2 tables to get data. The difference will be that Department will be populated in each row.
SELECT
d.dname AS Department
,e.Job
,e.ename AS Emp
FROM
departments d
LEFT OUTER JOIN emp e ON d.deptno = e.deptno
ORDER BY
d.dname
,e.Job
,2.ename
To get around this, you will have to tweak a bit:
SELECT
CASE WHEN RowNo = 1 THEN Department ELSE '' END AS Department
,Job
,Emp
FROM
(SELECT
d.dname AS Department
,e.Job
,e.ename AS Emp
,ROW_NUMBER() OVER (Partition By d.deptno ORDER BY e.Job,e.ename) RowNo
FROM
departments d
LEFT OUTER JOIN emp e ON d.deptno = e.deptno
) Result
ORDER BY
Result.RowNo

multiple columns in ALL clause

Is it possible to multiple columns with ALL clause.
please provide some examples
i tried this correlated query on HR shema.
SELECT *
FROM job_history j
WHERE ( employee_id, job_id ) = ALL (SELECT employee_id, job_id
FROM employees e
WHERE e.employee_id = j.employee_id
AND e.job_id = j.job_id)
it does not consider about inner query and where conditions.Instead of that it returns job_history table.
Lets assume the tables
job_history
employee_id job_id
----------- ----------
102 IT_PROG,
101 AC_ACCOUNT
employees
employee_id job_id
----------- ----------
102 IT_PROG,
So according to my knowledge the query should output only 102 IT_PROG,
But its output is
employee_id job_id
----------- ----------
102 IT_PROG,
101 AC_ACCOUNT
thats the problem..
Oracle optimizer changes ANY, ALL and SOME operators during run time with EXISTS / NOT EXISTS and shifts the filtering criteria inside the correlated query. I checked one example on one of the site...
SELECT e1.empno, e1.sal
FROM emp e1
WHERE e1.sal > ALL (SELECT e2.sal
FROM emp e2
WHERE e2.deptno = 20);
is interpreted by the compiler as
SELECT e1.empno, e1.sal
FROM emp e1
WHERE NOT EXISTS (SELECT e2.sal
FROM emp e2
WHERE e2.deptno = 20
AND e1.sal <= e2.sal)
Still not very sure but may be the query that you have provided is being interpreted as following:-
Original Query:
SELECT *
FROM job_history j
WHERE ( employee_id, job_id ) = ALL (SELECT employee_id, job_id
FROM employees e
WHERE e.employee_id = j.employee_id
AND e.job_id = j.job_id)
Oracle Interpretation(just a guess work based on > ANY example)
SELECT *
FROM job_history j
WHERE NOT EXISTS (SELECT employee_id, job_id
FROM employees e
WHERE e.employee_id = j.employee_id
AND e.job_id = j.job_id
and e.employee_id != j.employee_id
AND e.job_id != j.job_id)
But still there is long way to discover the actual interpretation by ALL, ANY and SOME.
Anyways, there clauses were used long before I guess before 8i and so, now a days they are considered as obsolete, although Oracle supports them but they have been taken over by analitical functions and other techniques of querying data.
It must be like this:
SELECT *
FROM job_history j
WHERE ( employee_id, job_id ) =ANY (SELECT employee_id, job_id
FROM employees e
WHERE e.employee_id = j.employee_id
AND e.job_id = j.job_id)
Opposite of =ANY is <>ALL.
x =ANY (SELECT ...) is equal to x IN (SELECT ...),
x <>ALL (SELECT ...) is equal to x NOT IN (SELECT ...)
Then you may try this one:
SELECT *
FROM job_history j
JOIN employees e USING (employee_id, job_id)