Oracle SQL querying for average across several tables - sql

Here are the relations:
CREATE TABLE employee (
name varchar2(15) not null,
ssn char(9),
sex char,
salary number(10,2),
dno number(4),
primary key (ssn),
foreign key (dno) references department(dnumber)
);
CREATE TABLE department (
dname varchar2(15) not null,
dnumber number(4),
primary key (dnumber),
unique (dname),
);
Q1: For each department whose average employee salary is more than $30,000, retrieve the department name and the number of employees working for that department.
Q2: Suppose that we want the number of male employees in each department rather than all employees (as in Q1) to calculate the departmental averages and number of employees. Can we specify this query in SQL? Why or why not.
Thanks!

Your first query might look like
SELECT d.dname,
COUNT(*) total_employees
FROM department d LEFT JOIN
employee e ON d.dnumber = e.dno
GROUP BY d.dname
HAVING AVG(e.salary) > 30000;
And the second one
SELECT d.dname,
COUNT(*) male_employees
FROM department d LEFT JOIN
employee e ON d.dnumber = e.dno
WHERE e.sex = 'M'
GROUP BY d.dname
HAVING AVG(e.salary) > 30000;
SQLFiddle

If you want to get the number of all employees and the number of male employees in one SQL,you can try this.
SELECT B.DNAME,
COUNT(*) AS TOTAL_EMPLOYEES,
COUNT(CASE WHEN A.SEX='M' THEN A.SEX END) AS MALE_EMPLOYEES,
AVG(A.SAL) AS ALL_AVG,
AVG(CASE WHEN A.SEX='M' THEN A.SAL END) AS MALE_AVG
FROM EMPLOYEE A JOIN DEPARTMENT B
ON A.DNO=B.DNUMBER
GROUP BY B.DNAME

SELECT dname, COUNT(*) AS Num_of_Employees
FROM employee, department
WHERE dno = dnumber AND EXISTS (
SELECT AVG(Salary)
FROM employee
GROUP BY dno
HAVING AVG(Salary)>30000)
GROUP BY dname;

Related

How to join multiple tables to get names of employees who earn more than their manager?

I have created two tables:
create table DEPARTMENTS
(
dept_id number,
dept varchar2(50) not null,
mgr_id number,
location varchar2(50),
constraint pk_departments primary key (dept_id)
);
create table EMPLOYEES
(
emp_id number,
fname varchar2(50) not null,
lname varchar2(50) not null,
email varchar2(50),
phone number,
hired date,
job_id varchar(20),
salary number(7,2),
commission_pct number(4,2),
dept_id number,
grade number,
constraint pk_employees primary key (emp_id),
constraint fk_employees_dept_id foreign key (dept_id)
references DEPARTMENTS (dept_id)
constraint fk_employees_grade foreign key (grade)
references SALGRADE (grade)
);
How go I get names of employees who earn more than their managers given that the mgr_id is in table departments and the employee is in employees table and they are linked by dept_id ?
I found that 'self-join' is needed. But this case is a bit difficult since referencing dept_id is needed to refer to the mgr_id then compare it to the emp_id and create join ?
For better clarity and self understanding, using ANSI joins:
SELECT * FROM
(SELECT E.SALARY AS MANAGER_SALARY, D.DEPT_ID AS DEPT_ID
FROM EMPLOYEES E
INNER JOIN
DEPARTMENTS D
ON E.DEPT_ID = D.DEPT_ID)
MGR_DET
INNER JOIN
EMPLOYEES EMP
ON MGR_DET.DEPT_ID = EMP.DEPT_ID
where EMP.SALARY > MGR_DET.MANAGER_SALARY;
You can do this with three joins. Start with the employees table, match to departments to get the manager. And then match back to employees using the manager.
This looks like:
select e.*, em.salary -- or whatever columns you want
from employees e join
departments d
on d.dept_id = e.dept_id join
employees em
on em.emp_id = d.mgr_id
where e.salary > em.salary
Answer with implicit joins:
SELECT E1.* FROM EMPLOYEES E1, DEPARTMENT D, EMPLOYEES E2
WHERE E1.DEPT_ID = D.DEPT_ID AND D.DEPT_ID = E2.DEPT_ID AND D.MGR_ID = E2.MGR_ID
AND E1.SALARY >= E2.SALARY;
Here the employee E1 is the employee whose salary is greater than that of his department manager.
To find that, we join the employee table E1 and department table D to get on dept_id and then fetch the mgr_id. Now the next step is to find the employee with the same ID as the mgr_id of the department. Hence we again join it with another employee table E2 on emp_id. Finally we compare the salary of employee e1 and the manager (employee e2).
Answer with explicit joins:
SELECT E1.* FROM EMPLOYEES E1 JOIN DEPARTMENT D ON E1.DEPT_ID = D.DEPT_ID
JOIN EMPLOYEES E2 ON (D.DEPT_ID = E2.DEPT_ID AND D.MGR_ID = E2.MGR_ID)
WHERE E1.SALARY >= E2.SALARY;

Select query in SQL SERVER need to support

I have 3 tables like that:
- Employee_Table (Employee_Number, Employee_Name, Department_Number)
With Employee_Number is PRIMARY KEY, Department_Number is FOREIGN KEY;
- Employee_Skill_Table (Employee_Number, Skill_Code, Date Registered)
With PRIMARY KEY (Employee_Number, Skill_Code);
- Department (Department_Number, Department_Name)
With PRIMARY KEY (Department_Number);
How can I specify
the departments which have >= 3 employees
Help me!
If you only need the departments that have more than 3 people try this query
SELECT d.Department_Name
FROM Department d
JOIN Employee_Table e ON e.Department_Number = d.Department_Number
GROUP BY Department_Name
HAVING COUNT(*) >= 3
SELECT dept.department_name
FROM employee_table emp
INNER JOIN department dept ON emp.department_number= dept.department_number
GROUP BY emp.department_number
HAVING COUNT(emp.department_number) >= 3

Find out those managers who manage only one employee

From default employees table I want to find managers who have only one employee
I have found only managers of employee
select e.first_name, e.last_name, e.manager_id
from employees e
inner join employees m on e.manager_id = m.employee_id;
result should show only employees having one manager
You could try cheking for having count(*) = 1 group by the manager_id
select manager_id
from employees
group by manager_id
having count(*) = 1
and if you need the name too
select e.first_name, e.last_name
from employees e
inner join (
select manager_id
from employees
group by manager_id
having count(*) = 1
) t on t.manager_id = e.employee_id
With this query:
select manager_id
from employees
group by manager_id
having count(*) = 1
you get all the managers who have only 1 employee.
If you need these employees:
select first_name, last_name, manager_id
from employees
where manager_id in (
select manager_id
from employees
group by manager_id
having count(*) = 1
)
You can hierarchical query for each manager( manager_id column ) to go deeply for all levels,
and filter by having count(distinct employee_id) = 1 to get managers who have only one employee.
select first_name, last_name, employee_id
from employees
where employee_id in
(
select manager_id
from employees
connect by prior employee_id = manager_id
start with manager_id in ( select distinct manager_id from employees )
group by manager_id
having count(distinct employee_id) = 1
);
Demo

JOIN or Conditional Logic Between 4+ Tables

Full disclosure, this is a homework question, but I have 20 more of these that are much more difficult, I just really can't wrap my head around some of the more complicated statements, so I'd appreciate the help before I just give up on the assignment completely. The professor and TA declined any help.
I'm using the 'HR' table that is present as a demo in all Oracle DBs. I need to create a unique listing of all jobs in a particular department along with the locations. It has the tables: REGIONS, LOCATIONS, DEPARTMENTS, JOBS, and EMPLOYEES of the form described below:
Locations:
Name Null? Type
----------------------------------------- -------- ----------------------------
LOCATION_ID NOT NULL NUMBER(4)
STREET_ADDRESS VARCHAR2(40)
POSTAL_CODE VARCHAR2(12)
CITY NOT NULL VARCHAR2(30)
STATE_PROVINCE VARCHAR2(25)
COUNTRY_ID CHAR(2)
Jobs:
Name Null? Type
----------------------------------------- -------- ----------------------------
JOB_ID NOT NULL VARCHAR2(10)
JOB_TITLE NOT NULL VARCHAR2(35)
MIN_SALARY NUMBER(6)
MAX_SALARY NUMBER(6)
Employees:
Name Null? Type
----------------------------------------- -------- ----------------------------
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
COMMISSION_PCT NUMBER(2,2)
MANAGER_ID NUMBER(6)
DEPARTMENT_ID NUMBER(4)
Departments:
Name Null? Type
----------------------------------------- -------- ----------------------------
DEPARTMENT_ID NOT NULL NUMBER(4)
DEPARTMENT_NAME NOT NULL VARCHAR2(30)
MANAGER_ID NUMBER(6)
LOCATION_ID NUMBER(4)
So of course I can think through how the relational algebra kind of goes, but I have no clue how it would go in SQL syntax. I of course know how to do more trivial joins, but I'm not understanding how to agregate all of the requirements into one statement. I want to get all UNIQUE or DISTINCT JOB_IDs where DEPARTMENT_ID=80,Then do a join on Jobs to get the matching JOB_TITLE,then derive LOCATION_ID from DEPARTMENTS.DEPARTMENT_ID and finally join Locations to my original JOB_TITLE selection WHERE LOCATION.LOCATION_ID=DEPARTMENTS.DEPARTMENT_ID. So what does that look like. Do I have to do 3 joins to get all the information in one selection?
This might help you. Add 'Where' condition as per your requirement.
select (select j.job_title from jobs j where j.JOB_ID=x.JOB_ID) as Job_Title ,
(select dp.department_name from department dp where dp.department_id) as Department_Name,
(select l.City || l.State_province from Location l where l.location_id = x.location_id) as Location
(select
e. JOB_ID,
D.DEPARTMENT_ID,
d.location_id
from Employees e inner join Department d on e.DEPARTMENT_ID = d.DEPARTMENT_ID
group by l.location_id,d.DEPARTMENT_ID , e.JOB_ID) x
This is intended to get you started. The core of your need comes from 2 tables employee (for job_id) and department (for location_id) and the relationship between employee and department is via the shared department_id column (this becomes the join)
SELECT
e.JOB_ID
, d.LOCATION_ID
FROM Employees e
INNER JOIN Departments d ON e.DEPARTMENT_ID = d.DEPARTMENT_ID
WHERE e.DEPARTMENT_ID = 80
Now you need to figure how to get the "uniqueness"
One way is to add select distinct and while this will work, it does limit your options
SELECT DISTINCT
e.JOB_ID
, d.LOCATION_ID
FROM Employees e
INNER JOIN Departments d ON e.DEPARTMENT_ID = d.DEPARTMENT_ID
WHERE e.DEPARTMENT_ID = 80
OR, to use GROUP BY which open up other possibilities such as aggregations
SELECT
e.JOB_ID
, d.LOCATION_ID
, COUNT(*) as count_of_employees
FROM Employees e
INNER JOIN Departments d ON e.DEPARTMENT_ID = d.DEPARTMENT_ID
WHERE e.DEPARTMENT_ID = 80
GROUP BY
e.JOB_ID
, d.LOCATION_ID
What I leave for you to discover is how to bring the names into the result.
Smth like that, perhaps
New-fashioned joins:
select tloc.*, tdept.*, tjobs.*
from locations tloc
inner join departments tdept on (tloc.location_id=tdept.location_id)
inner join employees templ on (tdept.department_id=templ.department_id)
inner join jobs tjobs on (templ.employee_id=tjobs.employee_id)
where
tdept.department_id=80
Old-fashioned joins:
select tloc.*, tdept.*, tjobs.*
from locations tloc, departments tdept, employees templ, jobs tjobs
where
tloc.location_id=tdept.location_id
and tdept.department_id=templ.department_id
and templ.employee_id=tjobs.employee_id
----
and tdept.department_id=80

List the department name with the least number of employees

There are two tables, employee and dept:
How can I join the two tables and select the department with the least number of employees?
It would have been better to answer if you provided the table structure.
But give this a try:
SELECT deptname
FROM dept
WHERE deptid = (SELECT distinct deptid
FROM employee
ORDER BY COUNT(dept) limit 1);
Please try the following...
SELECT deptid,
deptname,
employeeCount
FROM
(
SELECT dept.deptid AS deptid,
dept.deptname,
COUNT( dept.deptid ) AS employeeCount
FROM dept
JOIN employee ON dept.deptid = employee.deptid
GROUP BY dept.deptid,
dept.deptname
)
GROUP BY deptid
HAVING employeeCount = MIN( employeeCount );
This statement starts with the inner query joining dept to employee on the shared field deptid. It then groups the resulting rows by department and returns to the outer query the department's id and name along with a count of employees for that department.
The outer query groups the data by department again, then selects the details of the department(s) having a count of employees equal to the minimum count of employees.
If you have any questions or comments, then please feel free to post a Comment accordingly.
I'm only answering because MIN isn't needed and LIMIT isn't Oracle:
select d.*
from (select deptid, count(*) as cnt
from employees e
group by deptid
order by count(*) asc
) d
where rownum = 1;
In Oracle 12C+, you don't need the subquery:
select deptid, count(*) as cnt
from employees e
group by deptid
order by count(*) asc
fetch first 1 row only;
Hi :) so my answer is a bit ugly, and full of nested queries. but I tested it and it worked for me...
-- First I created a couple of test tables and added a few records
drop table dept;
drop table employee;
create table dept (deptid number primary key, deptname varchar(20));
create table employee(employee_id number primary key, names varchar(20),
deptid number,foreign key (deptid) references dept(deptid));
insert into dept values(1,'HR');
insert into dept values(2,'Finance');
insert into dept values(3,'IT');
insert into employee values(1,'Tina',1);
insert into employee values(2,'Rob',1);
insert into employee values(3,'Lisa',1);
insert into employee values(4,'Will',2);
insert into employee values(5,'Lina',2);
insert into employee values(6,'Ethel',2);
insert into employee values(7,'Trevor',1);
insert into employee values(8,'Alanea',1);
insert into employee values(9,'Matthew',1);
insert into employee values(10,'Maddie',3);
insert into employee values(11,'Anna',1);
-- According to the added records, the answer we are looking for should be
the department name IT
-- select the department name from department table
select d.deptname from dept d,
/* This is where it gets ugly - basically, it counts the number of
employees in each department, then finds the id of the department that had
the smallest count */
(select deptid from
(select count(deptid) as counter, deptid from employee group by deptid)
where counter =( select min(counter)from
(select count(deptid) as counter, deptid from employee group by deptid))) minid
-- join the tables using deptid
where d.deptid = minid.deptid;
This query gave the correct answer for me even when I changed the records to make finance the correct answer.
If you have any questions give me a yell through the comments :)
select department_name, count(employee_id)
from department d
inner join employee e
on d.employee_id = e.employee_id
having count(employee_id) =
(
select min(count(employee_id)) /*This query returns minimum count*/
from department d
inner join employee e
on d.employee_id = e.employee_id
group by department_name
)
group by department_name;
WITH temp
AS
(SELECT e1.department_id, count(e1.employee_id) emp_count
FROM hr.employees e1
GROUP BY e1.department_id)
SELECT d1.department_name, t1.emp_count employee_count
FROM temp t1
,hr.departments d1
WHERE t1.department_id = d1.department_id(+)
AND NOT EXISTS
(SELECT 1
FROM temp t2
WHERE t2.emp_count < t1.emp_count)
ORDER BY 2,1 ;