SQL select groups where values are exclusively in a list - sql

Given a table with two columns, department and employee, where every employee belongs to 1 department.
Given a list of employee ids, how do I select departments where all employees are in the list?
Department
Employee
finance
1
finance
2
marketing
3
marketing
4
IT
5
IT
6
given (2,3,4,5,6), returns ('marketing', 'IT')
(Note: DB flavor does not matter to me, you may use standard or DB-specific SQL)

Put your input id's into a table, join to the existing table, and check counts between input and the existing employees. If they match, output.
Change this to whatever RDBMS you're using
CREATE TEMP TABLE input_vals (id int);
insert into input_vals
values
(2),
(3),
(4),
(5),
(6);
with cte_joins AS (
select ot.department,
count(ot.employee) AS employee_count1,
sum(case when iv.id is not null then 1 else 0 end) AS employee_count2
from orig_table ot
left join input_vals iv
on ot.employee = iv.id
group by ot.department
)
select department
from cte_joins
where employee_count1 = employee_count2

You can use a couple of derived tables - one to get the count of employees in each department and one to get the count of employees in each department limited by your list of employees. Return the ones that match.
select
distinct full_list.department
from (
select
department,
count (*) as cnt
from
<your table>
group by department) full_list
inner join (
select
department,
count (*) as cnt
from
<your table>
group by department
where
employee in (2,3,4,5,6)
) limited_list
on full_list.department = limited_list.department
and full_list.cnt = limited_list.cnt

-- departments
select department from t where employee in (2,3,4,5,6);
-- departments containing extra persons
select department from t
where department in (select department from t where employee in (2,3,4,5,6))
and employee not in (2,3,4,5,6);
--- departments, employees from the list, with no extra
select department from t
where employee in (2,3,4,5,6) and department not in (
select department from t
where department in (
select department from t where employee in (2,3,4,5,6)
) and employee not in (2,3,4,5,6)
)

Assume you employee list is captured as a column in a table. Since you didn't provide any detail on the tool set you are using.
Something like this is a common pattern, depending on your dbms there's other more simple syntax.
select distinct
de.department
from department_employee de
where not exists
( select 1
from department_employee de2
where not exists
( select 1
from employee_list el
where de2.employee = el.employee
)
and de.department = de2.department
);

Related

SQL query which contains the order in which employees joined against each manager

I want to write a query against this table, such that it will return the list of employees in an order of date of joining as per the sequence of Manager_ID.
It should be able to tell the reportee that joined most recently for each manager.
You can try this:
SELECT *
FROM
<TABLE>
INNER JOIN
(
SELECT MANAGER_ID
, MAX(DATE_OF_JOINING) AS MAX_DATE
FROM <TABLE>
GROUP BY MANAGER_ID
) MAX_MANAGER
ON <TABLE>.MANAGER_ID = MAX_MANAGER.MANAGER_ID
AND <TABLE>.DATE_OF_JOINING = MAX_MANAGER.MAX_DATE
If two employees were hired the same day for the same manager you have two rows.

To display the name of the department that has the least student count in SQL

Tables:
Department (dept_id,dept_name)
Students(student_id,student_name,dept_id)
I am using Oracle. I have to print the name of that department that has the minimum no. of students. Since I am new to SQL, I am stuck on this problem. So far, I have done this:
select d.department_id,d.department_name,
from Department d
join Student s on s.department_id=d.department_id
where rownum between 1 and 3
group by d.department_id,d.department_name
order by count(s.student_id) asc;
The output is incorrect. It is coming as IT,SE,CSE whereas the output should be IT,CSE,SE! Is my query right? Or is there something missing in my query?
What am I doing wrong?
One of the possibilities:
select dept_id, dept_name
from (
select dept_id, dept_name,
rank() over (order by cnt nulls first) rn
from department
left join (select dept_id, count(1) cnt
from students
group by dept_id) using (dept_id) )
where rn = 1
Group data from table students at first, join table department, rank numbers, take first row(s).
left join are used is used to guarantee that we will check departments without students.
rank() is used in case that there are two or more departments with minimal number of students.
To find the department(s) with the minimum number of students, you'll have to count per department ID and then take the ID(s) with the minimum count.
As of Oracle 12c this is simply:
select department_id
from student
group by department_id
order by count(*)
fetch first row with ties
You then select the departments with an ID in the found set.
select * from department where id in (<above query>);
In older versions you could use RANK instead to rank the departments by count:
select department_id, rank() over (order by count(*)) as rnk
from student
group by department_id
The rows with rnk = 1 would be the department IDs with the lowest count. So you could select the departments with:
select * from department where (id, 1) in (<above query>);

list pair of employees that work in same department

in oracle SQL how can I list pair of employees that work in same department ... my employee table as follow:
employeeid department
101 2
102 2
103 3
104 3
105 4
You can get all employees in departments that have multiple employees by
SELECT * FROM employees
WHERE department IN
(
SELECT department
FROM employees
GROUP BY department
HAVING COUNT(*) > 1
)
Or use LISTAGG to show the IDs in a list:
SELECT
department,
LISTAGG(employeeid, ', ') AS EmployeeList
FROM employees
GROUP BY department
HAVING COUNT(*) > 1
If your table is called t, you can "list" the pairs of employees who work in the same department with a self-join, like this:
select t1.employeeid as employee1, t2.employeeid as employee2, department
from t t1 join t t2 using (department)
where t1.employeeid < t2.employeeid
Fair warning: for departments with a large number of employees, the number of pairs grows asymptotically like 1/2 times the square of the number of employees in that department, so you may get a very large number of rows on a "real" input table.
If needed, you can sort the results as needed, for example add this line:
order by department, employee1, employee2

Oracle Grouping and Duplicate Checking

I've been scratching my head on this one.
This question got me closer but my situation is a little more complex.
How do I find duplicate values in a table in Oracle?
Suppose I have a table EMPLOYEE and I want to know which employees work in multiple departments. My table has the employee id, and department they work in. When an employee works in multiple departments, their employee id will be listed as multiple records. I don't want to just count the employees that are listed twice. I need to know how many work in this list of departments vs this list of departments.
So for example if my table is:
Employee ID | Department
1 Accounting
1 Marketing
2 Accounting
3 Finance
4 Programming
And Department List A is
Accounting
Finance
And Department List B is
Marketing
Programming
Then the results of the query would be
Employee ID | Department
1 Accounting
1 Marketing
or
Employee ID | Count(Department)
1 2
since employee 1 works in a department from List A and List B.
I would approach this with a mapping:
with mapping as (
select 'Accounting' as department, 'A' as grouping from dual union all
select 'Finance' as department, 'A' as grouping from dual union all
select 'Marketing' as department, 'B' as grouping from dual union all
select 'Programming' as department, 'B' as grouping from dual
)
select employeeid, count(distinct m.grouping)
from t join
mapping m
on t.department = m.department
group by employeeid;
Note: you may want to store this information in another table or as an attribute in the Departments table.
If you want solo departments to map to themselves:
select t.employeeid, count(distinct coalesce(m.grouping, t.department))
from t join
mapping m
on t.department = m.department
group by t.employeeid;
If you want other departments not in the table to be grouped together:
select t.employeeid, count(distinct coalesce(m.grouping, 'UNKNOWN MAPPING'))
from t join
mapping m
on t.department = m.department
group by t.employeeid;
If I understand what you're after, you can use conditional aggregation in a HAVING clause for this:
SELECT Employee_ID, COUNT(DISTINCT Department) AS Dept_Count
FROM YourTable
GROUP BY Employee_ID
HAVING MAX(CASE WHEN Department IN ('Accounting','Finance') THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN Department IN ('Marketing','Programming') THEN 1 ELSE 0 END) = 1

Find the name of the employees, whose names are same but salary are different

I have an 'employee' table with
create table employee
(
e_number int,
e_name varchar(20),
salary money,
hire_date date
)
Now I want to display only the name of the employees who have the same name but different salary.
I tried select e_name,count(*) from employee group by e_name having count(*)>1;
but cannot combine it with "the same salary" section. Any help?
This assumes that you want the names of both of the people listed:
SELECT e1.e_name
FROM employee e1, employee e2
WHERE e1.e_name = e2.e_name
AND e1.salary <> e2.salary;
If you only want each name listed once, you would use a SELECT DISTINCT instead of the SELECT.
If your goal is to express this in the having clause:
Select name
from employee
group by name
having
count(*) > 1
and min(salary) != max(salary)
order by name
SELECT employee1.e_name, employee1.Salary, Employee2.Salary
FROM Employee employee1
JOIN Employee employee2
on employee1.name = employee2.name
AND Employee1.Salary <> Employee2.Salary
AND Employee1.E_Number <> employee2.E_Number
Basically get every employee, join it to every other employee via name, where the employee number is different (so don't join to yourself) and salary is different.
You probably don't need to check that employee number is different because 1 employee can only have 1 salary in your table design
Use a join, but importantly use a greater-than comparison, rather than a not-equals, to avoid duplicates:
SELECT e1.e_name as name1, e2.e_name as name2
FROM employee e1
JOIN employee e2 ON e1.e_name = e2.e_name
AND e1.salary > e2.salary;
Note also that the ON condition contains the salary comparison. It is a common misconception that the join on condition may only contain key-related comparisons. Doing this can have significant performance benefits, especially when further joins are made, because the ON condition is executed as the rows are joined - which discards non-matches immediately, whereas WHERE conditions are executed as a filter on the entire result set of the joins.
You just want the count of distinct salaries, not the count of all records.
select e_name,count(distinct salary)
from employee
group by e_name
having count(distinct salary)>1
(Drop the count in the select if unneeded - included since it was in your example)
First filter salary not double (not in), then grouping by e_name having count > 1
SELECT A.e_name
FROM employee A
WHERE A.salary NOT IN (SELECT salary FROM employee WHERE id != A.id)
GROUP BY A.e_name
HAVING COUNT(A.e_name) > 1