Aggregate function and join on 2 conditions - sql

I have this task for Oracle. It requires me to display the departments names(department is a table containing the manager_id for that table, department_id and department_name), the manager_id for that department, the name of the manager(found in employees table) and the average salary for that department(salary of each employee is also found in employees). As soon as I try to retrieve the manager name(I suppose by comparing his id in dept with the one in employees) it messes up my averages. Without it(Like following) it works just fine
SELECT d.department_name, AVG(e.salary) as "Salaries"
FROM employees e join departments d on e.department_id=d.department_id
WHERE d.manager_id=e.employee_id
GROUP BY e.department_id, d.department_name,d.manager_id
ORDER BY AVG(e.salary)
Can someone help me solve this and perhaps explain why I mess it up?

WITH factoring clause selects average salary; it is then joined to other tables
SQL> with avgsal as
2 (select e.department_id, round(avg(e.salary)) avg_sal
3 from employees e
4 group by e.department_id
5 )
6 select d.department_id,
7 d.department_name,
8 m.last_name manager,
9 a.avg_sal
10 from departments d
11 join employees m on m.department_id = d.department_id
12 and m.employee_id = d.manager_id
13 join avgsal a on a.department_id = d.department_id
14 order by a.avg_sal;
DEPARTMENT_ID DEPARTMENT_NAME MANAGER AVG_SAL
------------- -------------------- ------------------------- ----------
50 Shipping Fripp 3476
30 Purchasing Raphaely 4150
10 Administration Whalen 4400
60 IT Hunold 5760
40 Human Resources Mavris 6500
100 Finance Greenberg 8601
80 Sales Russell 8956
20 Marketing Hartstein 9500
70 Public Relations Baer 10000
110 Accounting Higgins 10154
90 Executive King 19333
11 rows selected.
SQL>
A quick check for 2 departments:
SQL> select department_id, round(avg(salary)) avg_sal, count(*) "# of employees"
2 from employees
3 where department_id in (20, 30)
4 group by department_id
5 order by 1;
DEPARTMENT_ID AVG_SAL # of employees
------------- ---------- --------------
20 9500 2
30 4150 6
SQL>

Introduce a correlated subquery in select for manager name in your query.
SELECT d.department_name,
AVG(e.salary) AS "Salaries",
(SELECT first_name
||' '
||last_name
FROM employees i
WHERE i.employee_id = d.manager_id) AS manager_name
FROM employees e
join departments d
ON e.department_id = d.department_id
GROUP BY e.department_id,
d.department_name,
d.manager_id
ORDER BY Avg(e.salary);

Related

find emp_names,max,min salary and number of employees on each department?

I use oracle 11g , so i have 2 tables(employees,departments):
desc employees: desc departments
EMPLOYEE_ID DEPARTMENT_ID
FIRST_NAME DEPARTMENT_NAME
LAST_NAME MANAGER_ID
EMAIL LOCATION_ID
PHONE_NUMBER
HIRE_DATE
JOB_ID
SALARY
COMMISSION_PCT
MANAGER_ID
DEPARTMENT_ID
and i want to get
employee_name,emp_names,emp_salary,dep_id,dep_names,max salary on each
dep , and min salary on each dep , and number of employees on each
department .
so i do this qouery:
select FIRST_NAME,DEPARTMENT_ID,max(SALARY),min(SALARY),count(EMPLOYEE_ID)
from employees join departments on employees.department_id = departments.departm
ent_id group by first_name,department_id;
but its give an error:
ERROR at line 1: ORA-00918: column ambiguously defined
however does my sql query right ?
I don't have your tables so I created views out of Scott's ones, to simulate what you have.
SQL> create or replace view employees as
2 select empno employee_id,
3 ename last_name,
4 deptno department_id,
5 sal salary
6 from emp;
View created.
SQL> create or replace view departments as
2 select deptno department_id,
3 dname department_name
4 from dept;
View created.
SQL>
Here's how I understood the question: list of employees per department should be separated from the rest (minimums, maximums, counts).
So: list of employees:
SQL> select d.department_name, e.last_name
2 from departments d join employees e on d.department_id = e.department_id
3 order by d.department_name;
DEPARTMENT_NAM LAST_NAME
-------------- ----------
ACCOUNTING CLARK
ACCOUNTING KING
ACCOUNTING MILLER
RESEARCH JONES
RESEARCH FORD
RESEARCH ADAMS
RESEARCH SMITH
RESEARCH SCOTT
SALES WARD
SALES TURNER
SALES ALLEN
SALES JAMES
SALES BLAKE
SALES MARTIN
14 rows selected.
Aggregates: outer join for departments that don't have any employees:
SQL> select d.department_name,
2 min(e.salary) min_sal,
3 max(e.salary) max_sal,
4 count(e.employee_id) cnt_emp
5 from departments d left join employees e on d.department_id = e.department_id
6 group by d.department_name
7 order by d.department_name;
DEPARTMENT_NAM MIN_SAL MAX_SAL CNT_EMP
-------------- ---------- ---------- ----------
ACCOUNTING 1300 5000 3
OPERATIONS 0
RESEARCH 800 3000 5
SALES 950 2850 6
LISTAGG allows you to list all employees per department in the same statement, though; see line 5. I, somehow, doubt that you learnt about that function yet (as you struggle with such a problem).
SQL> select d.department_name,
2 min(e.salary) min_sal,
3 max(e.salary) max_sal,
4 count(e.employee_id) cnt_emp,
5 listagg(e.last_name, ', ') within group (order by e.last_name) employees
6 from departments d left join employees e on d.department_id = e.department_id
7 group by d.department_name
8 order by d.department_name;
DEPARTMENT_NAM MIN_SAL MAX_SAL CNT_EMP EMPLOYEES
-------------- ---------- ---------- ---------- -------------------------------------------
ACCOUNTING 1300 5000 3 CLARK, KING, MILLER
OPERATIONS 0
RESEARCH 800 3000 5 ADAMS, FORD, JONES, SCOTT, SMITH
SALES 950 2850 6 ALLEN, BLAKE, JAMES, MARTIN, TURNER, WARD
SQL>

SQL - counting max number of employees and excluding string using subqueries

These are my two tables:
employee
employee_id employee_name job manager_id hire_date salary commission department_id
----------------------------------------------------------------------- -------------------------------------
7839 KING PRESIDENT 20-NOV- 01 5000 50
7596 JOST VICE PRESIDENT 7839 04-MAY- 01 4500 50
7603 CLARK VICE PRESIDENT 7839 12-JUN- 01 4000 50
7566 JONES PUBLIC ACCOUNTANT 7596 05-APR- 01 3000 10
7886 STEEL PUBLIC ACCOUNTANT 7566 08-MAR- 03 2500 10
7610 WILSON ANALYST 7596 03-DEC- 01 3000 20
7999 WOLFE ANALYST 7610 15-FEB- 02 2500 20
7944 LEE ANALYST 7610 04-SEP- 06 2400 20
7900 FISHER SALESMAN 7603 06-DEC- 01 3000 500 30
7921 JACKSON SALESMAN 7900 25-FEB- 05 2500 400 30
7952 LANCASTER SALESMAN 7900 06-DEC- 06 2000 150 30
7910 SMITH DATABASE ADMINISTRATOR 7596 20-DEC- 01 2900 40
7788 SCOTT PROGRAMMER 7910 15-JAN- 03 2500 40
7876 ADAMS PROGRAMMER 7910 15-JAN- 03 2000 40
7934 MILLER PROGRAMMER 7876 25-JAN- 02 1000 40
8000 BREWSTER TBA 22-AUG- 13 2500
department
department_id department_name address
------------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 IT DALLAS
50 EXECUTIVE NEW YORK
60 MARKETING CHICAGO
I need to write a statement using subqueries (not joins) to display the name and address of all departments (excluding departments in Dallas) having the maximum number of employees. I cannot have any hard coding except the string 'DALLAS'
The result should look like this:
DEPARTMENT_NAME ADDRESS
-------------------- --------------------
EXECUTIVE NEW YORK
SALES CHICAGO
I'm at a loss on doing this without joins. Here are my three tries:
SELECT department_name, address
FROM department
WHERE department_id NOT IN
(SELECT department_id
FROM department
WHERE UPPER(address) + 'DALLAS')
ORDER BY department_name;
SELECT department_name, address
FROM department
WHERE department_id NOT IN
(SELECT department_id
FROM department
WHERE UPPER(address) = 'DALLAS')
AND department_id > ALL
(select COUNT(department_id) FROM employee GROUP BY department_id)
ORDER BY department_id;
select *
from department
where department_id in (select department_id
from employee
group by department_id
having count(*) = (select max(num)
from (select department_id,
count(*) as num
from employee_tbl
where address != 'DALLAS'
group by department_id)));
Can anyone PLEASE tell me if I'm even getting close and guide me in the right direction? Thanks
The departments with the maximum number of employees (using a single table scan):
SELECT department_id
FROM (
SELECT department_id,
RANK() OVER ( ORDER BY num_employees DESC ) AS rnk
FROM (
SELECT department_id,
COUNT(1) AS num_employees
FROM employees
GROUP BY department_id
)
)
WHERE rnk = 1;
Finding the departments not in DALLAS:
SELECT department_id
FROM departments
WHERE address <> 'DALLAS'
So combining the two:
SELECT department_id
FROM (
SELECT department_id,
RANK() OVER ( ORDER BY num_employees DESC ) AS rnk
FROM (
SELECT department_id,
COUNT(1) AS num_employees
FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE address <> 'DALLAS'
)
GROUP BY department_id
)
)
WHERE rnk = 1;
maybe something like this?
select department_name, address
from department
where department_id in
(SELECT e.department_id
FROM employee e
group by e.department_id
having count(*) = (
select max(count(e2.employee_id))
FROM employee e2
WHERE department_id NOT IN (SELECT department_id
FROM department
WHERE UPPER(address) = 'DALLAS')
group by e2.department_id
)
)
and UPPER(address) != 'DALLAS'
but it is not the best query in plane :) very strange
In your query Ellen I did not understand where from the table
employee_tbl

select how many DEPTNO's exists for each LOC plus number of EMP's

I guess messed up the joins...
Single DEPT has many LOCations, every DEPT should have a sum of its EMPs. I need to show DISTINCT LOCs, number of LOCs per DEPT and sum of EMPS in each DEPT - without dividing the locations.
With tables DEPT & EMP like this:
SELECT DEPTNO, DNAME, LOC FROM DEPT;
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
50 NONE DALLAS
select ENAME, DEPTNO from EMP;
SMITH 20
ALLEN 30
WARD 30
JONES 20
MARTIN 30
BLAKE 30
CLARK 10
SCOTT 20
KING 10
TURNER 30
ADAMS 20
JAMES 30
FORD 20
MILLER 10
CURT 40
Can't properly add the below subquery into my WITH... clause below. Need to select how many DEPTNO's exists for each LOC plus give a number of EMP's in each DEPT like here (plus location number) - it should use 2 different kinds of summaries in:
select e.deptno, count(e.deptno) from emp e
group by e.deptno;
10 3
20 5
30 6
40 1
Here is what I did:
WITH workers_per_dept as
(SELECT
d.LOC LOC,
d.deptno DEPTNO,
count(e.empno) EMP_NUMBER
FROM dept d
LEFT OUTER JOIN emp e ON (e.deptno = d.deptno)
GROUP BY d.LOC,d.deptno
ORDER BY d.deptno)
select
d.LOC LOCATION,
count(d.LOC) LOCATIONS_PER_DEPT,
workers_per_dept.EMP_NUMBER
from DEPT d, workers_per_dept
WHERE d.LOC = workers_per_dept.LOC
GROUP BY d.LOC, workers_per_dept.EMP_NUMBER
ORDER BY 1;
I receive this (should be groupped by LOC with):
BOSTON 1 1
CHICAGO 1 6
DALLAS 2 0
DALLAS 2 5
NEW YORK 1 3
(result should have not repeated LOC's - 'DALLAS 2 0' should be skipped)
Here is the query. You need to group by location and count distinct occurrences of Dept's in each group:
select d.loc,
count(distinct d.deptno) depts,
count(e.ename) emps
from dept d
left join emp e on d.deptno = e.deptno
group by d.loc
Stupid me... but finally did it myself
it was so easy...
SELECT d.LOC LOCATION,
(select count(d.LOC) FROM DEPT l WHERE l.LOC=d.LOC) LOCS,
(select count(e.deptno) FROM EMP e WHERE e.DEPTNO = d.DEPTNO) EMPS
FROM DEPT d;
Thanks for help!!!

SQL - select name and address of all departments having max number of employees

I have these tables:
department
DEPARTMENT_ID DEPARTMENT_NAME ADDRESS
------------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 IT DALLAS
50 EXECUTIVE NEW YORK
60 MARKETING CHICAGO
employee
Employee_ID employee_name job manager_ID hire_date salary commission department_ID
------------------------------------------------------------------------------------------------------------
7839 KING PRESIDENT 20-NOV-01 5000 50
7596 JOST VICE PRESIDENT 7839 04-MAY-01 4500 50
7603 CLARK VICE PRESIDENT 7839 12-JUN-01 4000 50
7566 JONES PUBLIC ACCOUNTANT 7596 05-APR-01 3000 10
7886 STEEL PUBLIC ACCOUNTANT 7566 08-MAR-03 2500 10
7610 WILSON ANALYST 7596 03-DEC-01 3000 20
7999 WOLFE ANALYST 7610 15-FEB-02 2500 20
7944 LEE ANALYST 7610 04-SEP-06 2400 20
7900 FISHER SALESMAN 7603 06-DEC-01 3000 500 30
7921 JACKSON SALESMAN 7900 25-FEB-05 2500 400 30
7952 LANCASTER SALESMAN 7900 06-DEC-06 2000 150 30
7910 SMITH DATABASE ADMINISTRATOR 7596 20-DEC-01 2900 40
7788 SCOTT PROGRAMMER 7910 15-JAN-03 2500 40
7876 ADAMS PROGRAMMER 7910 15-JAN-03 2000 40
7934 MILLER PROGRAMMER 7876 25-JAN-02 1000 40
8000 BREWSTER TBA 22-AUG-13 2500
I need to display the name and address with the exception of Dallas, having the maximum number of employees.
I have written this:
SELECT department_name, address
FROM department
WHERE department_id IN
(SELECT MAX(department_id)
FROM department
WHERE UPPER(address) != 'DALLAS')
ORDER BY department_name;
But I'm only getting one line
DEPARTMENT_NAME ADDRESS
-------------------- --------------------
MARKETING CHICAGO
What am I doing wrong?
Not sure I understood your question, but I think you want this:
with emp_count as (
select d.department_name,
d.address,
count(*) as num_emps,
max(count(*)) over () as max_count
from department d
join employee e on d.department_id = e.department_id
where address <> 'DALLAS'
group by d.department_name, d.address
)
select *
from emp_count
where num_emps = max_count;
This returns:
DEPARTMENT_NAME | ADDRESS | NUM_EMPS
----------------+----------+---------
SALES | CHICAGO | 3
EXECUTIVE | NEW YORK | 3
SQLFiddle example: http://sqlfiddle.com/#!4/05db83/1
Based on your sample data the city seems to be recorded in the address column of the department table, and that more than one department can have the same city.
Given this, and the fact that you want to "exclude Dallas," as you say, I would assume you want to filter out all departments where the associated address column is 'Dallas'.
However, I don't know if...
you want to exclude departments at Dallas if they happen to be tied with other department(s) for the most employees, or
if you want to exclude departments at Dallas from being considered when determining what the highest number of employees is at any department.
If (1) is true:
select *
from department_tbl
where department_id in (select department_id
from employee_tbl
group by department_id
having count(*) = (select max(num_emps)
from (select department_id,
count(*) as num_emps
from employee_tbl
group by department_id)))
and address <> 'DALLAS';
If (2) is true:
select *
from department_tbl
where department_id in (select department_id
from employee_tbl
group by department_id
having count(*) = (select max(num_emps)
from (select department_id,
count(*) as num_emps
from employee_tbl
where address <> 'DALLAS'
group by department_id)));
Try this.
SELECT d.department_name,
d.address
FROM department d
JOIN employee e
ON ( d.department_id = e.department_id )
WHERE d.address <> 'DALLAS'
GROUP BY d.department_name,
d.address
HAVING Count(*) = (SELECT Max(cnt)
FROM (SELECT Count(*) CNT
FROM employee e
WHERE NOT EXISTS (SELECT 'x'
FROM department d
WHERE d.department_id =
e.department_id
AND d.address = 'DALLAS')
GROUP BY department_id))
select e.department_id, d.department_name, count(e.department_id)
from employee e, department d
where e.department_id = d.department_id
group by e.department_id, d.department_name
having count(e.department_id)=(select max(count(department_id))
from employee
group by department_id);
Hope it helps

Department names(with employee name) with more than 2 employee and salary greater than 90% of respective department average salary

I am trying to generate a SQL query to find the Department names(with employee name) with more than 2 employee whose salary greater than 90% of respective department average salary. My SQL Code is working fine , it has no Syntax errors but the output is giving me additional data. The table is as follos
JONES ACCOUNTING 3000
STEEL ACCOUNTING 2500
WILSON RESEARCH 3000
WOLFE RESEARCH 2500
LEE RESEARCH 2400
LANCASTER SALES 2000
JACKSON SALES 2500
FISHER SALES 3000
ADAMS IT 2000
MILLER IT 1000
SCOTT IT 2500
SMITH IT 2900
KING EXECUTIVE 5000
JOST EXECUTIVE 4500
CLARK EXECUTIVE 4000
My code is as follows.
Select department_name , employee_name
from department d , employee e
where e.department_id = d.department_id
and (SELECT COUNT(*)
FROM Employee E
WHERE E.department_ID = D.department_ID) > 2
and salary >
0.9*(SELECT ROUND(AVG(salary),2)
FROM employee e_inner
WHERE e_inner.department_id = e.department_id);
I notice that my code returns the value of department with more than 2 employees and salary > 90% of department's average salary. whereas I am looking for departments with more than 2 employees whose salary is more than 90% of department avg salary
I think this should do it:
select *
from (
select department_name,
employee_name,
sum(case when salary > avg_dept_sal * 0.9 then 1 else 0 end) over (partition by department_id) as greater_count
from (
select d.department_name,
e.department_id,
e.employee_name,
e.salary,
count(*) over (partition by e.department_id) as dept_count,
avg(salary) over (partition by e.department_id) as avg_dept_sal
from employee e
join department d on e.department_id = d.department_id
) t1
) t2
where greater_count >= 2
This will return all employees of those departments. If you only want to see those employees whose salary is actually greater than the 90% you need to add another condition to the outer where clause to only select those.
The below stated query will give result as desired without using any analytics. Simple & Easy just like English.
**
*
with T as (select empname, deptno, salary from employee e1 where
salary > (select avg(salary)*0.9 from employee e2 where
e2.deptno=e1.deptno group by deptno having count(empname)>2)) select
T.empname, deptname, salary from T, department where
T.deptno=department.deptno;
*
**
I executed this query successfully on Oracle database.