Get MAX element based on two different tables - sql

I have problem with SQL query on Oracle DB.. I have following tables:
DEPARTMENT(`ID` NUMBER(11), `NAME` VARCHAR(25))
EMPLOYEE(`ID` INT(11), `LASTNAME` VARCHAR(25), `DEP_ID` INT(11));
SALARIES(`ID` INT(11), `EMPLOYEE_ID` INT(11), `SALARY` INT(11));
Now, I want to get name of depratment with highest average sum of salary. Department isn't directly related to Salaries so probably I need to use Employee table as well.
I've created a query:
SELECT NAME, (SELECT SUM(SALARIES.SALARY) FROM SALARIES JOIN EMPLOYEE ON EMPLOYEE.EMPLOYEE_ID = EMPLOYEE.ID WHERE EMPLOYEE.DEP_ID = DEPARTMENT.ID GROUP BY EMPLOYEE.ID) AS AVG_OF_SUM FROM DEPARTMENT;
It returns list of department's name and avg sum. But now I need to get only one department name for the highest averange row.
Is my query actually OK? Or can be improved? And how can I get only one record?
Thanks for any help.
Regards,
D

Make use of the ANALYTIC function SUM...OVER
In the subquery, apply the analytic function, and then select only those rows which you desire.
For example,
SELECT DISTINCT DEPT, SUM(SAL) OVER (PARTITION BY DEPT ORDER_BY DEPT) SUM_SAL
FROM EMPLOYEE
ORDER_BY DEPT;

SELECT NAME, (SELECT MAX(SUM(SALARIES.SALARY))
FROM SALARIES
JOIN EMPLOYEE ON EMPLOYEE.EMPLOYEE_ID = EMPLOYEE.ID )
WHERE EMPLOYEE.DEP_ID = DEPARTMENT.ID
GROUP BY EMPLOYEE.ID) AS AVG_OF_SUM FROM DEPARTMENT;

SELECT NAME, avg_sal FROM
(SELECT d.NAME, avg(s.SALARY) avg_sal
FROM SALARIES s
JOIN EMPLOYEE e ON s.EMPLOYEE_ID = e.ID
JOIN DEPARTMENT d ON e.DEP_ID = d.ID
GROUP BY d.NAME
ORDER BY 2 DESC)
WHERE rownum = 1;
(This query shows a department with the highest avg salary. If you need sum replace AVG -> SUM)

Related

Write a query to display the name of the department that has the maximum student count

this is the schema Write a query to display the name of the department that has the maximum student count.
this is what is tried.
select d.department_name,count(s.student_id)
from department d left join student s
on d.department_id=s.department_id
group by d.department_name,d.department_id
order by d.department_name;
and i think there is something missing in my code
You're almost there.
Order the result in descending order on the number of students and then take the first row:
SELECT department_name
FROM
(
SELECT d.department_name,
COUNT(*) AS nr_students
FROM department d
JOIN student s
ON d.department_id = s.department_id
GROUP BY d.department_name
ORDER BY nr_students DESC
)
WHERE ROWNUM <= 1;
Based on the schema mentioned, you would have to make a join (INNER JOIN) to the department table from the staff table to get the name of the department.
If the name of the department is not desired and the counts can just be based on the department_id, then a join is not required.
The queries for both the scenarios is mentioned below.
Oracle SQL query for result with the department name, i.e. with INNER JOIN
SELECT D.DEPARTMENT_NAME, COUNT(S.DEPARTMENT_ID) AS STAFF_COUNT FROM **DEPARTMENT D, STAFF S** --INDICATES INNER JOIN IN ORACLE SQL
WHERE D.DEPARTMENT_ID = S.DEPARTMENT_ID
GROUP BY D.DEPARTMENT_NAME
ORDER BY STAFF_COUNT DESC
Oracle SQL query for result without the department name, just the department_id
SELECT S.DEPARTMENT_ID,COUNT(S.DEPARTMENT_ID) AS STAFF_COUNT FROM STAFF S
GROUP BY S.DEPARTMENT_ID
ORDER BY STAFF_COUNT DESC
Hope this helps. Cheers.
I tried this and it worked.
select department_name
from department d inner join student s
on s.department_id=d.department_id
having count(*) in (
select max(count(student_id))
from student s join department d
on d.department_id=s.department_id
group by d.department_id)
group by d.department_id,department_name;
Select * from (
SELECT D.DEPARTMENT_NAME, COUNT(S.DEPARTMENT_ID) AS STAFF_COUNT
FROM DEPARTMENT D, STAFF S
WHERE D.DEPARTMENT_ID = S.DEPARTMENT_ID
GROUP BY D.DEPARTMENT_NAME
ORDER BY STAFF_COUNT DESC)
where rownum=1;
This query will give department name that has maximum number of student count

The best way to find the Department with the maximum total salary Postgresql

Lets we have 2 standard tables Employees and Departments
CREATE TABLE departments (
id SERIAL PRIMARY KEY,
name VARCHAR
);
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
department_id INTEGER,
name VARCHAR,
salary NUMERIC(13,2)
);
What is the best way to find the name of the department with the maximum employees' total salary.
I've found two solutions and they looks too complicated for such simple task.
Using rank()
SELECT name FROM (
SELECT name, rank() OVER ( ORDER BY salary DESC ) AS rank
FROM (
SELECT
departments.name,
sum(salary) AS salary
FROM employees
JOIN departments ON department_id = departments.id
GROUP BY departments.name
) AS t1
) AS t2
WHERE rank = 1;
Using subquery
WITH t1 AS (SELECT
departments.name,
sum(salary) AS salary
FROM employees
JOIN departments ON departments.id = employees.department_id
GROUP BY departments.name
)
SELECT name FROM t1
WHERE t1.salary = (SELECT max(salary) FROM t1);
At first glance using rank should be less efficient as it performs unnecessary sorting. Though EXPLAIN shows that the first option is more efficient.
Or maybe someone suggests another solution.
So, what is the best way to find the Department with the maximum total salary using postgres?
I would write the rank() as:
SELECT *
FROM (SELECT d.name, SUM(e.salary) AS salary,
RANK() OVER (ORDER BY SUM(e.salary)) as rnk
FROM employees e JOIn
departments d
ON e.department_id = d.id
GROUP BY d.name
) d
WHERE rnk = 1;
(The additional subquery should not affect performance, but it adds nothing to clarify the query either.)
Because window functions are built-in to the database, the database has methods for making them more efficient. And there is overhead for getting the MAX() as well. But, to be honest, I would expect both methods to have similar performance.
I should note that if you want only one department returned -- even when there are ties -- then the simplest method is:
SELECT d.name, SUM(e.salary) AS salary
FROM employees e JOIn
departments d
ON e.department_id = d.id
GROUP BY d.name
ORDER BY SUM(e.salary) DESC
FETCH FIRST 1 ROW ONLY

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 ;

Inner Join: Is this an optimal solution?

T1: employee [id, salary]
T2: department [name, employeeid]
(employeeid is a foreign key to T1's id)
Problem: Write a query to fetch the name of the department which receives the maximum salary.
My Solution:
SELECT DISTINCT name
FROM department AS a
INNER JOIN employee AS b ON a.employeeid = b.id
AND b.salary
IN (
SELECT max( salary )
FROM employee AS c
)
Edit: The problem statement is accurate, and we're not trying to find out the employee who has the highest salary. It says "....Department which receives.....", not "...employee who receives....".
Is this ok? Or can this be optimized?
GROUP BY the name of the department and order by SUM(salary).
SELECT department.name
FROM department
JOIN employee ON department.employeeid = employee.id
GROUP BY department.name
ORDER BY SUM(salary) DESC
LIMIT 1
How about:
SELECT employee.id, employee.salary, department.name
FROM department, employee
where
employee.id = department.employeeid and
employee.salary = (select max(salary) from employee)

Extra Fields with SQL MIN() & GROUP BY

When using the SQL MIN() function, along with GROUP BY, will any additional columns (not the MIN column, or one of the GROUP BY columns) match the data in the matching MIN row?
For example, given a table with department names, employee names, and salary:
SELECT MIN(e.salary), e.* FROM employee e GROUP BY department
Obviously I'll get two good columns, the minimum salary and the department. Will the employee name (and any other employee fields) be from the same row? Namely the row with the MIN(salary)?
I know there could very possibly be two employees with the same (and lowest) salary, but all I'm concerned with (now) is getting all the information on the (or a single) cheapest employee.
Would this select the cheapest salesman?
SELECT min(salary), e.* FROM employee e WHERE department = 'sales'
Essentially, can I be sure that the data returned along with the MIN() function will matches the (or a single) record with that minimum value?
If the database matters, I'm working with MySql.
If you wanted to get the "cheapest" employee in each department you would have two choices off the top of my head:
SELECT
E.* -- Don't actually use *, list out all of your columns
FROM
Employees E
INNER JOIN
(
SELECT
department,
MIN(salary) AS min_salary
FROM
Employees
GROUP BY
department
) AS SQ ON
SQ.department = E.department AND
SQ.min_salary = E.salary
Or you can use:
SELECT
E.*
FROM
Employees E1
LEFT OUTER JOIN Employees E2 ON
E2.department = E1.department AND
E2.salary < E1.salary
WHERE
E2.employee_id IS NULL -- You can use any NOT NULL column here
The second statement works by effectively saying, show me all employees where you can't find another employee in the same department with a lower salary.
In both cases, if two or more employees have equal salaries that are the minimum you will get them both (all).
SELECT e.*
FROM employee e
WHERE e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = 'sales'
ORDER BY
e.salary
LIMIT 1
)
To get values for each department, use:
SELECT e.*
FROM department d
LEFT JOIN
employee e
ON e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = d.id
ORDER BY
e.salary
LIMIT 1
)
To get values only for those departments that have employees, use:
SELECT e.*
FROM (
SELECT DISTINCT eo.department
FROM employee eo
) d
JOIN
employee e
ON e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = d.department
ORDER BY
e.salary
LIMIT 1
)
Of course, having an index on (department, salary) will greatly improve all three queries.
The fastest solution:
SET #dep := '';
SELECT * FROM (
SELECT * FROM `employee` ORDER BY `department`, `salary`
) AS t WHERE IF ( #dep = t.`department`, FALSE, ( #dep := t.`department` ) OR TRUE );
Another approach can be using Analytical functions. Here is the query using analytical and ROW_NUM functions
select first_name, salary from (select first_name,salary, Row_NUMBER() over (PARTITION BY DEPARTMENT_ID ORDER BY salary ASC) as row_count from employees) where row_count=1;