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
Related
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
);
I wrote a query joining two tables and I got a below resultset:
SELECT emp.employee_id,
dept.department_name,
dept.department_id
FROM employee emp,
department dept
WHERE emp.department_id = dept.department_id;
Employee_ID Department Department_ID
Mark Sales D1
Mark Marketing D2
Justin Textiles D3
Kimberley (null) (null)
However, I need to display below output with one new field called 'Status'.Mark can work in both the departments and so the count is "2" and the status will be 'Y' ( displaying of any one record is okay) . Justin works in only one department and count is 1 and status should be 'N'. Kimberley does not work anywhere and count is 0 and status should be 'N'.
Expected output:
Employee_ID Department Department_ID Status
Mark Sales D1 Y
Justin Textiles D3 N
Kimberley (null) (null) N
Please help.
I understand that you want to display the first department per user, and add a flag that indicates whether the employee belongs to at least one other department.
You can use window functions:
select
employee_id,
department_name,
department_id
case when cnt <= 1 then 'N' else 'Y' end status
from (
select
emp.employee_id,
dept.department_name,
dept.department_id,
row_number() over(partition by emp.employee_id order by dept.department_id) rn,
count(*) over(partition by emp.employee_id) cnt
from
employee emp
left join department dept on emp.department_id = dept.department_id
) t
where rn = 1
Side note: always use explicit joins (with the on keyword) instead of old-school, implicit joins (with commas in the from clause), whose syntax is harder to read and maintain.
I think this can be easily achieved using group by and keep clause with max as following:
SELECT emp.employee_id,
Max(dept.department_name) keep (dense_rank first order by dept.department_id) as department_name,
Max(dept.department_id) keep (dense_rank first order by dept.department_id) as department_id,
case when count(1) > 1 then 'Y' else 'N' end as status
FROM employee emp
LEFT JOIN department dept ON emp.department_id = dept.department_id
GROUP BY emp.employee_id;
Cheers!!
So, this is the exercise:
Make a query that displays the number of presidents (job_id AD_PRES), the total salary sum of the presidents, the number of administration vice presidents (job_id AD_VP), and the total salary sum of those vice presidents.
The way it was shown to me:
Select count(decode(job_id,'AD_PRES',1,0)) AS NumOfPres,
Sum(decode(job_id,'AD_PRES',salary,0) AS SumSalaryP,
count(decode(job_id,'AD_VP',1,0)) AS NumOfPres,
Sum(decode(job_id,'AD_VP',salary,0) AS SumSalaryVP
FROM EMPLOYEES;
This is what I did:
Select count (e.job_id),sum(e.salary),count(m.job_id),sum(m.salary)
FROM employees e join employees m on(e.job_id=m.job_id)
Where e.job_id='AD_PRES'
AND m.job_id='AD_VP';
So, join is more readable I would say but is there any other performance difference?
Your join is forcing the relationship between the employees. If there is no such relationship then all results would return 0.
Also you are linking all employees that are president with all employees that are vice-president. So if you have 3 presidents and 10 vicepresidents, the join will result in 30 rows and the sum will be repeated to calculate the total, something that would never happen in the first query.
The exercise didn't specify return a single row, so the simple way is:
Select
job_id,
count(*),
sum(salary)
FROM EMPLOYEES
where job_id in ('AD_PRES', 'AD_VP')
group by job_id
The 1st Select uses legacy syntax and will not return a correct result as count(decode(job_id,'AD_PRES',1,0)) is the same as count(*).
And it's reading all rows, there should be a WHERE:
Select
sum(case when job_id = 'AD_PRES' then 1 else 0 end) AS NumOfPres,
Sum(case when job_id = 'AD_PRES' then salary else 0 end) AS SumSalaryP,
sum(case when job_id = 'AD_VP' then 1 else 0 end) AS AS NumOfVPs,
Sum(case when job_id = 'AD_PRES' then salary else 0 end) AS SumSalaryVP
FROM EMPLOYEES
where job_id in ('AD_PRES', 'AD_VP');
Your 2nd Select doesn't work at all. You join on matching job_ids, but one is 'AD_PRES' and the other 'AD_VP', so no match.
When you want to join it would be a CROSS JOIN:
Select *
from
( Select
count(*) AS NumOfPres,
sum(salary) AS SumSalaryP
FROM EMPLOYEES
where job_id ='AD_PRES'
)
cross join
( Select
count(*) AS NumOfVPs,
sum(salary) AS SumSalaryVP
FROM EMPLOYEES
where job_id ='AD_VP'
)
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
I hava the sql as below:
select a.dept, a.name
from students a
group by dept, name
order by dept, name
And get the result:
dept name
-----+---------
CS | Aarthi
CS | Hansan
EE | S.F
EE | Nikke2
I want to summary the num of students for each dept as below:
dept name count
-----+-----------+------
CS | Aarthi | 2
CS | Hansan | 2
EE | S.F | 2
EE | Nikke2 | 2
Math | Joel | 1
How shall I to write the sql?
Although it appears you are not showing all the tables, I can only assume there is another table of actual enrollment per student
select a.Dept, count(*) as TotalStudents
from students a
group by a.Dept
If you want the total count of each department associated with every student (which doesn't make sense), you'll probably have to do it like...
select a.Dept, a.Name, b.TotalStudents
from students a,
( select Dept, count(*) TotalStudents
from students
group by Dept ) b
where a.Dept = b.Dept
My interpretation of your "Name" column is the student's name and not that of the actual instructor of the class hence my sub-select / join. Otherwise, like others, just using the COUNT(*) as a third column was all you needed.
select a.dept, a.name,
(SELECT count(*)
FROM students
WHERE dept = a.dept)
from students a
group by dept, name
order by dept, name
This is a somewhat questionable query, since you get duplicate copies of the department counts. It would be cleaner to fetch the student list and the department counts as separate results. Of course, there may be pragmatic reasons to go the other way, so this isn't an absolute rule.
SELECT dept, name, COUNT(name) as CT from students
group by dept, name
order by dept, name
This should do it (I haven't got any environment to test on at the min)
select a.dept, a.name, count(a.*) as NumOfStudents
from students a
group by dept, name order by dept, name
HTH
Or Otherwise write simply
select dept, name, count(name) as nostud from students group by dept, name order by dept, name
This will give the results requested above
select a.dept, a.name, cnt
from student a
join (
select dept, count(1) as cnt
from student
group by dept
) b on b.dept = a.dept