Is there a way to make this SQL more efficient? - sql

Consider the following tables:
department
deptid (type:INT)
deptname (type: TEXT)
hours (type:INT)
active (type:BIT)
employee
empid (type:INT)
empname (type: TEXT)
deptid (type: INT)
designation (type: TEXT)
salary (type: INT)
Write a query to return the columns empname and deptname of the employees belonging to those
departments that have a head count of 4 or more. The records should be returned in alphabetical order of empname
This was my take:
SELECT e1.empname, d.deptname from employee AS e1
FULL JOIN department AS d on e1.deptid = d.deptid
WHERE e1.deptid IN(
SELECT deptid FROM(
SELECT e2.deptid, COUNT(e2.empid)
FROM employee AS e2
GROUP BY e2.deptid
HAVING COUNT(e2.empid) >= 4
)
)
ORDER BY empname;
How would you improve on this?

This is shorter and probably performs faster too
SELECT e1.empname, d.deptname
from (
SELECT e2.deptid
FROM employee AS e2
GROUP BY e2.deptid
HAVING COUNT(e2.empid) >= 4
) G
inner join employee AS e1 on e1.deptid = G.deptid
INNER JOIN department AS d on d.deptid = G.deptid
ORDER BY e1.empname;
Start with the grouping. You don't need COUNT from the inner query.
Then, join to both tables just to get the names.
INNER JOIN is used because once the count is complete, we already know that
the employees exist
the department exists

Try this query, it will work properly.
select empname,deptname from employee,department
where
employee.deptid=department.deptid and employee.deptid
in
(
select deptId from employee group by deptid having count(*)>=4
)
order by empname

Related

How to get result if exactly one match inner join

How can I write a query to join two tables and return result if exactly one match in there. I have to discard results if zero match and more than one match.
All I am looking for is to extend the INNER JOIN. Let me just get to the point. I have two tables Dept & Emp. One Dept can have multiple Emp's & not the other way around.
Table Dept
Table Emp
I need to JOIN it on Dept_id
Expected Results
You can join with a not exists condition:
select d.*, e.emp_id, e.emp_name
from dept d
inner join emp e
on d.dept_id = e.dept_id
and not exists (
select 1
from emp e1
where e1.dept_id = d.dept_id and e1.emp_id != e.emp_id
)
One alternative to existing solutions can be one using analytics (window functions),
instead of joining twice:
select dept_id, dept_name, emp_id, emp_name
from
(
SELECT
d.Dept_id, d.Dept_name, e.Emp_id, e.Emp_Name,
count(*) over (partition by d.dept_id) cnt1
FROM d
INNER JOIN e
ON d.Dept_id = e.Dept_id
) where cnt = 1;
You could use a subquery for group by dept_id haing count = 1
select t.dept_id, dept.dept_name, emp.Emp_name
from (
select dept_id
from emp
group by dept_id
having count(*) = 1
) t
INNER JOIN dept on t.dept_id = dept.dept_id
INNER JOIN emp ON t.dept_id = emp.dept_id
You can phrase this as an aggregation query in Oracle:
select d.dept_id, d.dept_name,
max(e.emp_id) as emp_id,
max(e.emp_name) as emp_name
from dept d inner join
emp e
using (dept_id)
group by d.dept_id, d.dept_name
having count(*) = 1;
This works because if there is only one match, then max() returns the value from the one row.
Also, try below query;
SELECT a.depid dept_id,dept_name,emp_id,emp_name
FROM
(SELECT case WHEN count(*)=1 THEN dept_id END depid FROM emp GROUP BY dept_id) a INNER JOIN emp ON depid=dept_id
INNER JOIN dept b ON a.depid = b.dept_id
WHERE depid IS NOT NULL
Another way would be
select d.dept_id, d.dept_name, e.emp_name
from emp e
join dept d on d.dept_id = e.dept_id
where e.dept_id in
( select dept_id from emp group by dept_id having count(*) = 1 )

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 ;

SQL JOIN : Help using join inisted subquery

I have 2 tables,
The first one is called emp, and has 2 columns called id and name
The second one is called dep, and has columns called id and empid and nameOfDep
if I want to list all emp that have X dep, but don't have Y dep
This is an example I use
Select e.id, e.name
from emp e
where e.id in (Select empid from deptid where deptid=X)
and e.id not in (Select empid from deptid where deptid=Y);
How I can make it using JOIN instead of with subqueries?
An IN can be converted into an INNER JOIN. A Not IN can be converted to LEFT JOIN / NULL Test. Sometimes called an ANTI JOIN.
SELECT e.id,
e.name
FROM emp e
INNER JOIN deptid D_X
ON e.empid = d_x.empid
AND deptid = 'X'
LEFT JOIN deptid D_Y
ON e.empid = d_Y.empid
AND deptid = 'Y'
WHERE d_Y.empid IS NULL
Also I'm making the assumption that when you wrote deptid = X that you meant X to be a literal string and not a field name
SELECT e.id, e.name
FROM emp e
INNER JOIN dep d ON (e.deptID = d.deptID AND d.deptID NOT y)
Add the Department ID to the employee record and then join on that.
EDIT
My bad, updated.
EDIT
Helps to read, go with Conrad's answer.

SELECT specific information

My tables are structured like this (there are more values in the tables but I only wrote the ones relevant to this):
Department(dep_id, dep_name)
Employee(dep_id)
I need to display dep_name and the number of employees in every department, except once specific department (let's call it DepX) and only the departments with more than one employee.
I tried multiple methods to solve this but none of them worked.
Some methods I tried:
SELECT department.dep_name, COUNT(employee.dep_id) AS NumberOfEmployees FROM employee
INNER JOIN department ON employee.dep_id=department.dep_id
WHERE dep_name<>'DepX'
GROUP BY dep_id
HAVING COUNT(employee.dep_id) > 1;
SELECT dep_name FROM department
WHERE dep_name <>'DepX'
UNION
SELECT COUNT(*) FROM employee
WHERE COUNT(*) > 1
GROUP BY dep_id;
I can't figure this out. Thanks!
The first example does now work because you're including dep_name in your results without an aggregation but not grouping on it.
You can either use the department name in your grouping instead of the ID:
SELECT department.dep_name, COUNT(employee.dep_id) AS NumberOfEmployees FROM employee
INNER JOIN department ON employee.dep_id=department.dep_id
WHERE dep_name<>'DepX'
GROUP BY department.dep_name
HAVING COUNT(employee.dep_id) > 1;
or do the COUNT in a subquery:
SELECT department.dep_name,
e.NumberOfEmployees
FROM department
INNER JOIN (SELECT dep_id,
COUNT(*) NumberOfEmployees
FROM employee
GROUP BY dept_id
HAVING COUNT(dept_id) > 1
) e
ON department.dep_id = e.dep_id
WHERE dep_name<>'DepX'
SELECT department.dep_name, COUNT(employee.dep_id) AS NumberOfEmployees FROM employee
INNER JOIN department ON employee.dep_id=department.dep_id
WHERE department.dep_name not in('DepX')
GROUP BY department.dep_name
HAVING COUNT(employee.dep_id) > 1;
update your table alias per your need
TEST this. This query help you return not only dept_name, it can return all fields from Department if you want:
SELECT d.*, A.numOfEmployees
FROM Department d,
(
SELECT e.dep_id, COUNT(*) numOfEmployees
FROM Employee e
GROUP BY e.dep_id
HAVING COUNT(*) > 1
) A
WHERE d.dep_id = A.dep_id
AND d.dep_name != 'DepX'

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)