I want to get data from 3 tables - sql

I have 3 tables employee, jobs and department as below,
------------------- ---------------- ------------------
| employee | | jobs | | department |
------------------- ---------------- ------------------
| empId | | jobId | | deptId |
| fname | | jobTitle | | deptName |
| lname | | | | managerId |
| jobId | | | | |
| managerId | | | | |
| departmentId | | | | |
------------------- ---------------- -------------------
I want to select all data from employee, the job title through jobId, the deptName through deptId and manager name through managerId and employeeId
SELECT EMPLOYEES.EMPLOYEE_ID, EMPLOYEES.FIRST_NAME, EMPLOYEES.LAST_NAME,
EMPLOYEES.JOB_ID, JOBS.JOB_TITLE AS JOB_TITLE, EMPLOYEES.SALARY,
DEPARTMENTS.DEPARTMENT_ID, DEPARTMENTS.DEPARTMENT_NAME AS DEPARTMENT_NAME
FROM EMPLOYEES
LEFT JOIN JOBS ON EMPLOYEES.JOB_ID = JOBS.JOB_ID
LEFT JOIN DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
what should I do to get the name of the manager using the empId?
or is there any other way to simplify this block of code?

Just Add another LEFT JOIN to the EMPLOYEES table, but give it an alias, say MANAGERS.
SELECT
EMPLOYEES.EMPLOYEE_ID,
EMPLOYEES.FIRST_NAME,
EMPLOYEES.LAST_NAME,
EMPLOYEES.JOB_ID,
JOBS.JOB_TITLE AS JOB_TITLE,
EMPLOYEES.SALARY,
DEPARTMENTS.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME AS DEPARTMENT_NAME,
MANAGERS.FIRST_NAME AS MANAGER_FIRST_NAME,
MANAGERS.LAST_NAME AS MANAGER_LAST_NAME
FROM EMPLOYEES
LEFT JOIN EMPLOYEES MANAGERS
ON EMPLOYEES.MANAGER_ID = MANAGERS.EMPLOYEE_ID
LEFT JOIN JOBS
ON EMPLOYEES.JOB_ID = JOBS.JOB_ID
LEFT JOIN DEPARTMENTS
ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID

You can use another left join on employee table to get the manager name.
SELECT (
EMPLOYEES.EMPLOYEE_ID,
EMPLOYEES.FIRST_NAME,
EMPLOYEES.LAST_NAME,
EMPLOYEES.JOB_ID,
JOBS.JOB_TITLE AS JOB_TITLE,
EMPLOYEES.SALARY,
DEPARTMENTS.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME AS DEPARTMENT_NAME,
(MGR.FIRST_NAME + ' ' + MGR.LAST_NAME) AS MANAGER_NAME)
FROM EMPLOYEES
LEFT JOIN JOBS
ON EMPLOYEES.JOB_ID = JOBS.JOB_ID
LEFT JOIN DEPARTMENTS
ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
LEFT JOIN EMPLOYEES MGR
ON EMPLOYEES.MANAGERID = MGR.EMPLOYEE_ID

try using aliases and join back to employee on the manager id.
SELECT
E.EMPLOYEE_ID, E.FIRST_NAME, E.LAST_NAME,
E.JOB_ID, J.JOB_TITLE AS JOB_TITLE, E.SALARY,
D.DEPARTMENT_ID, D.DEPARTMENT_NAME AS DEPARTMENT_NAME,
M.EMPLOYEE_ID as mgr_id, M.FIRST_NAME as mgr_name, M.LAST_NAME as mgr_lname
FROM EMPLOYEES E LEFT JOIN JOBS J ON
E.JOB_ID = J.JOB_ID
LEFT JOIN DEPARTMENTS D ON
E.DEPARTMENT_ID = D.DEPARTMENT_ID
join employees M ON
E.manager_ID = M.employee_ID
The reason this query works is because I am doing a self join back to the Employees table from the main result set that you already established. To put it simply, imagine you made an exact copy of the employees table and called it M. If you took your original query and joined to the M table using the original query's Employee.manager_id to the employee_ID in the M table, then you would have the manager for the employee.
There is no need to create an exact copy of the employee table just to look up the manager. We can just reference the employee table a second time and use an alias (I used M for manager). Then we join from your list of employees using the manager_id to get the employee's manager.
You could do this again to get the manager's manager if you need to. Here is that query:
SELECT
E.EMPLOYEE_ID, E.FIRST_NAME, E.LAST_NAME,
E.JOB_ID, J.JOB_TITLE AS JOB_TITLE, E.SALARY,
D.DEPARTMENT_ID, D.DEPARTMENT_NAME AS DEPARTMENT_NAME,
M.EMPLOYEE_ID as mgr_id, M.FIRST_NAME as mgr_name, M.LAST_NAME as mgr_lname
FROM EMPLOYEES E LEFT JOIN JOBS J ON
E.JOB_ID = J.JOB_ID
LEFT JOIN DEPARTMENTS D ON
E.DEPARTMENT_ID = D.DEPARTMENT_ID
join employees M ON /* The employee's manager */
E.manager_ID = M.employee_ID
LEFT join employees MM ON /* The employee's manager's manager */
M.manager_ID = MM.employee_ID
I used a left join for this last one, because at some point you will get to the top of the management hierarchy and might find that there are no more managers. You could also put a left join on the join employees M
Here it is in Tabular form
Employee_id | Name | manager_id
1 | Fred | 10
2 | Jane | 10
10 | Bob | 20
20 | Betty | Null
Looking at employee # 1. The values of E.employee_id = 1, E.Name = Fred and E.manager_id = 10.
So the relevant lines of the query evaluate as follows:
...
join employees M ON /* The employee's manager */
E.manager_ID /* i.e. 10 */ = M.employee_ID
...
So the M alias now refers to the employee record where M.employee_ID = 10 and as such, M.Name = Bob and M.employee_id = 20.
Using the last version of the query, we could then work out that Fred's manager's manager (i.e. Fred's manager is Bob and Bob's manager is Betty) will have an employee_id of 20 (i.e. M.manager_ID = 20), so the MM.employee_id would be 20 and hence refer to Betty who doesn't seem to have a manager.

Related

Can't find no employees in each department

DEPARTMENT Table
DEPARTMENT_ID
| 1 |
| 2 |
EMPLOYEES Table
EMPLOYEE_ID | DEPARTMENT_ID
ANDY | 1
Output
DEPARTMENT_ID|
2
Here is my code:
SELECT DEPARTMENT_ID
FROM DEPARTMENTS
where department_id!= all ( SELECT department_id
FROM employees
);
The code doesn't show 2,and the output is blank.
Like this:
DEPARTMENT_ID|
If you want to find the number of each employee in each department, you should use a left join aggregation:
SELECT d.DEPARTMENT_ID, COUNT(e.EMPLOYEE_ID) AS cnt
FROM DEPARTMENT d
LEFT JOIN EMPLOYEES e
ON d.DEPARTMENT_ID = e.DEPARTMENT_ID
GROUP BY d.DEPARTMENT_ID;
The above count expression counts the EMPLOYEE_ID column in the employee table. It is important to note that should a department have no employees, then COUNT would return zero, since NULL is not counted.
SELECT d.department_id
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
WHERE e.department_id IS NULL
SQLFiddle Demo

Employee Table and Manager Table

I have Employee Table
EMPID | EMPNAME
1 | A
2 | B
I have [Manager Table]
MNGID | EMPID
2 | 1
The above MNGID Refers TO Employee Table Means B is the Manager of A.
I wanted query to display Manager Name and Employee Name. Please Sugget Result
This should do the job for you:
select E.EmpName as EmployeeName, EM.EmpName as ManagerName
FROM Employee E
join Manager M on E.EMPID = M.EMPID
join Employee EM on M.MngID = EM.EMPId
try this[Updated query]:
SELECT
m1.empname as manager,
e.empname as employee
FROM
Manager m
JOIN
Employee e
ON
m.empid = e.empid
JOIN
Employee m1
ON
m1.empid = m.mngid
================
http://www.sqlfiddle.com/#!9/61560b/1

How to Improve This Self-Joins

I am learning Oracle SQL by working with its primitive HR schema where there is EMPLOYEES table which has three columns that I'm mainly interested in: MANAGER_ID, which is basically a self reference to EMPLOYEES.EMPLOYEE_ID, DEPARTMENT_ID, and SALARY. (You can find the schema diagram and schema objects here).
I wish, for each employee, to retrieve his/her SALARY, alongside of employee's manager's departmental average salary. For instance, if we have the following (EMPLOYEE_ID = 140 is the interested party here):
+-------------+--------+---------------+------------+
| EMPLOYEE_ID | SALARY | DEPARTMENT_ID | MANAGER_ID |
+-------------+--------+---------------+------------+
| 140 | 12000 | 50 | 110 |
| 110 | 20000 | 60 | 101 |
| 156 | 18000 | 60 | 101 |
| 175 | 15000 | 60 | 105 |
| 320 | 24000 | 60 | 105 |
+-------------+--------+---------------+------------+
I am interested in obtaining an average salary of all the managers (not all other non-managerial employees) in department where employee's manager works at (in this case, DEPARTMENT_ID =60), and compare it with employee's (in this case, 140). In a sample data above, the output should be:
+-------------+--------+-------------+-------------+------------+
| EMPLOYEE_ID | SALARY | AVG_MGR_SAL | MGR_DEPT_ID | MANAGER_ID |
+-------------+--------+-------------+-------------+------------+
| 140 | 12000 | 19250 | 60 | 110 |
+-------------+--------+-------------+-------------+------------+
where we have four (4) managers working in department 60, and $19250 being calculated as (20000 + 18000 + 15000 + 24000) / 4. I have come up with the following query that seems to work (and excludes those employees that don't have a manager):
select
employee_id
, salary employee_salary
, trunc(mgr_info.avg_manager_salary_per_dept, 0) emp_manager_avg_sal_dept
, mgr_info.manager_dept_id
, mgr_info.manager_id
from employees
join (
select
e1.employee_id manager_id
, e1.department_id manager_dept_id
, e1.salary manager_salary
, avg(e1.salary) over (partition by e1.department_id) avg_manager_salary_per_dept
from employees e1
join (
select distinct manager_id
from employees
where manager_id is not null
) mgr_ids
on e1.employee_id = mgr_ids.manager_id
) mgr_info
on employees.manager_id = mgr_info.manager_id
order by employee_id
However, I feel like that there should be a better way of getting the same result with fewer self-joins. Is there a way to get a better performance?
Something like this... You only need one join, you can compute the average salary for the manager's department on the "manager" copy of the table. I only included a few columns, you may need more, or fewer, but I believe the core of what you wanted is covered.
(NOTE: Edited since I realized I missed one detail in the requirement)
select e.employee_id as employee_id,
e.salary as employee_salary,
m.employee_id as manager_id,
m.department_id as manager_dept_id,
m.avg_salary as avg_sal_of_mgr_dept
from hr.employees e inner join
( select employee_id, department_id,
avg(salary) over (partition by department_id) as avg_salary
from hr.employees
where employee_id in (select manager_id from hr.employees)
) m
on e.manager_id = m.employee_id
;
Here is an option which uses a series of joins to get your result:
SELECT DISTINCT t1.EMPLOYEE_ID,
t1.SALARY,
t1.DEPARTMENT_ID,
COALESCE(t2.SALARY, 0.0) AS ManagerAvgSal
FROM employees t1
LEFT JOIN
(
SELECT e1.DEPARTMENT_ID, AVG(e1.SALARY) AS SALARY
FROM employees e1
WHERE e1.EMPLOYEE_ID IN (SELECT DISTINCT MANAGER_ID FROM employees)
GROUP BY e1.DEPARTMENT_ID
) t2
ON t1.DEPARTMENT_ID = t2.DEPARTMENT_ID

How do I join two results with avg()?

I have two query results:
Select avg(salary), department_id
from employees
Group by department_id
and
Select d.department_name, e.department_id
From departments d, employees e
Where e.department_id=d.department_id
How can I combine the into one result. Dept. ID on first is primary key for both tables
I tried union, creating new tables... Nothing works I need something like this:
Dept_id | Dept_name | avg_salary |
---------------------------------------
121 | Management | 324000 |
---------------------------------------
102 | Tax | 432555 |
---------------------------------------
Etc
I am assuming you want avarage salary for each department. For that you shoudl use the JOIN with group by. And AVG aggregate to get the average salary for the department.Try this,
SELECT
d.department_id,
d.department_name,
AVG(e.Salary) AS avarageSalary
FROM
Department d JOIN Employee e
ON d.department_id= e.department_id
GROUP BY
d.department_id,d.department_name

sql query for getting data from two related tables

I have two tables, employee and inventory. One employee can have zero or more inventories.
I would like to list employee information along with at most one inventory information
and count of inventories belongs to one employee.
employee table
emp_num last_name first_name
-----------------------------------
100 john smith
101 mike pet
102 jes lyoid
inventory table
inv_num emp_num
---------------------------
12 100
13 100
15 100
30 102
desired Output
emp_num last_name invnum count(inv_num)
--------------------------------------------------------------------------
100 john 12 3
101 mike - 0
102 jes 30 1
What sql query can I use in this case?
Try this:
SELECT emp_num, last_name, MAX(inv_num) AS invnum, COUNT(inv_num) AS inv_count
FROM employee e LEFT OUTER JOIN inventory i ON e.emp_num = i.emp_num
GROUP BY e.emp_num, e.last_name
You could do something like this
Select E.Emp_Num,
e.Last_name,
MIN(Inv_Num) AS OldestInv,
COUNT(Inv_Num) AS TotalInv
FROM Employee E
LEFT OUTER JOIN Inventory I
(E.Emp_Num = I.Emp_Num)
GROUP BY E.Emp_Num, E.Last_Name
This will give you the minimum invoice number and the total count. The left outer join is the key
SELECT
e.emp_num,
e.last_name,
IFNULL(MAX(i.inv_num),'-') AS 'invnum',
COUNT(i.inv_num) AS 'count(inv_num)'
FROM
employee e LEFT JOIN inventory i
ON e.emp_num = i.emp_num
GROUP BY
e.emp_num, e.last_name