CTE to Group Employees that are in different departments Many to Many - sql

Can anyone help please! I just cant seem to solve this puzzle?
Input Table
DepartmentID EmpID
-----------------------
1 100
1 101
1 103
1 200
2 300
2 350
3 350
3 100
4 50
4 30
4 45
5 50
5 51
5 52
5 53
6 53
6 54
7 54
7 55
8 55
8 56
10 800
11 900
Output Table
Please note that GroupID is created by us to group departments that have common employees, with the condition that 1 employee cannot be in two departments.
GroupID Department
-----------------------
1000 1
1000 2
1000 3
1001 4
1001 5
1001 6
1001 7
1001 8
1002 10
1003 11
Example to show how and why Department 1, 2 & 3 are grouped:
EmpID 100 is common between Department 1 & 3, but wait! EmpID 350 is common between 2 & 3 as well. So group them as well. Now the Group created by departments 1, 2 and 3 do not any product that is in any other department, then we can stop.
Note: This is not a 'normal' group by because we dont want any 2 groups that we create to have same employee.
LOGIC:
Step1: EmpID 50 is common between department 4 & 5. So Group 4 & 5 together
Step2: So this means Group of 4 & 5 have 50,30,45,51,52,53 unique employees
Step3: But wait! the Department 6 has EmpID 53 common with the Group of 4 & 5, formed in step2
Step4: Group department 4, 5, and 6. This new group has unique employees of 50,30,45,51,52,53,54
Step5: But wait! the department 7 has EmpID of 54, which is common with the Group formed in step4. So Group them together
This goes on.... Till we don`t have any employee that is not in 2 groups. So in this case Group 7, Group 8 will also need to be 'merged' into the Group that is mentioned in Step 4.

This is a graph traversal problem that requires recursive CTEs. I think this is one approach:
with cte as (
select department, empid
from inputs
union all
select cte.department, i.empid
from inputs i join
cte
on i.empid = cte.empid and i.department <> cte.department
)
select department,
row_number() over (order by min(empid)) as groupid
from cte
group by deparment;

Related

SQL get all children

So I have hierarchical data that is organized in departments, with each department having a parent department except for the top one. I now want to write a SELECT statement to select this hierarchical structure in a cumulative way. That means that for every level in the hierarchy I want to see all the entries that are children of that.
For example, if I have the following tables:
Departments
ID PARENT_ID
1 null
2 1
3 1
4 2
5 2
6 3
7 3
Employees
ID DEPT
1 2
2 2
3 3
4 4
5 5
6 6
7 7
8 2
9 3
10 4
11 5
12 6
13 7
14 2
15 3
16 4
17 5
I would like to have something like the following result:
ID_E ROOT DEPT
1 null 1
1 1 2
2 null 1
2 1 2
3 null 1
3 1 3
4 null 1
4 1 2
4 2 4
5 null 1
...
I have looked around and fiddled a bit but just cannot get it to work.
I thought this would do the trick but it gives weird results (meaning many duplicate rows):
SELECT connect_by_root(dept.id) AS dept_id,
CONNECT_BY_ROOT(dept.parent_id) AS parent_id,
emp.id AS id_e
FROM emp
RIGHT JOIN dept ON emp.dept = dept.id
CONNECT BY PRIOR dept.id = dept.parent_id
EDIT: Here is a fiddle of the scenario
I came up with a solution, using a recursive CTE to parse the hierarchy and retrieve each possible way a department can connect to the root, which is then joined with the employee table.
Could you give it a try, and let me know if it works?
WITH RCTE_DEPT(id,root,parent_id) AS(
SELECT id,parent_id, id
FROM dept
UNION ALL
SELECT dept.id,root,RCTE_DEPT.parent_id
FROM dept
JOIN RCTE_DEPT ON
dept.parent_ID = RCTE_DEPT.id)
SELECT emp.id as ID_E, RCTE_DEPT.root as ROOT, RCTE_DEPT.parent_id as DEPT
FROM emp
JOIN RCTE_DEPT ON emp.DEPT = RCTE_DEPT.id
ORDER BY ID_E ASC, ROOT ASC, DEPT ASC
Here is a fiddle.

Partition by two columns and find sum SQL

Assume there's a table students where we have student id, course id, role id, number of failed assignments and total number of assignments of the students in the course.
student role course fail total
1000 23 20022 1 5
1000 23 10055 2 8
1000 23 29000 0 10 fail = 2, total = 23
-------------------------------
1000 15 50003 1 7
1000 15 10299 3 8 fail = 4, total = 15
-------------------------------
1000 34 30042 0 5 fail = 0, total = 5
------------------------------------------
2035 34 90002 1 10
2035 34 55053 2 8 fail = 3, total = 18
-------------------------------
2035 10 80003 0 5 fail = 0, total = 5
...
What is the way to partition by two columns to find total number of fails and total number of assignments per each role for each student?
Expected output for above example would be:
student role sum_fail sum_total
1000 23 2 23
1000 15 4 15
1000 34 0 5
----------------------------------------
2035 34 3 18
2035 10 0 5
...
The code I tried so far produces incorrect numbers where each student have exactly the same number of fails and total per role:
SELECT s.student, s.sum_fail, s.sum_total
FROM ( SELECT student, role, SUM(fail) OVER (PARTITION BY role) AS sum_fail,
SUM(total) OVER (PARTITION BY role) AS sum_total
FROM students ) s
WHERE s.student=student
GROUP BY s.student, s.sum_fail, s.sum_total;
You want one result row per student and role. "per" translates to GROUP BY in SQL. With two mere sums, this is a simple aggregation:
select student, role, sum(fail) as sum_fail, sum(total) as sum_total
from students
group by student, role
order by student, role;

Make a query to display unmatched data

I m having difficulty making a query that displays unmatched data in between 2 tables.
The tables are:
Employee data Salary data
ID Holidays ID Holiday
1 10 0 10
2 8 1 5
3 5 2 8
4 7 3 5
5 8 7 6
6 5 8 9
7 6 9 2
8 9 10 3
the primary key is ID for both tables.
I want my query result to contain all the values that does not match in both tables.
The type of output i want is something like this:
ID Holiday
0 10
1 10
1 5
4 7
5 8
6 5
9 2
10 3
I tried using unmatched query wizard but that only compares ID, not the Holiday column.
Please help me!
You could use a union of two exists queries:
SELECT ID, Holiday FROM Employee as e
WHERE NOT EXISTS (SELECT 1 FROM Salary as s WHERE s.ID = e.ID AND s.Holiday = e.Holiday)
UNION ALL
SELECT ID, Holiday FROM Salary as s
WHERE NOT EXISTS (SELECT 1 FROM Employee as e WHERE e.ID = s.ID AND e.Holiday = s.Holiday);

Queries to fetch items based on top Ranks

I have a table which rank the items which i have.
I need a queries which will pick up only the top 2 ranks for a given item, the rank may not be in sequential order.
I need to fetch the item with least two ranks, there will same rank for two items as well.
Here is the snap shot of my table.
Item Id Supp Id Rank
1 2 2
1 1 7
1 7 5
1 9 11
2 67 4
2 9 14
2 10 14
2 34 4
2 25 3
2 60 3
2 79 5
my requirement is if I enter 2 i should get the result as below
Item Id Supp_id Rank
2 25 3
2 60 3
2 67 4
2 34 4
I am using oracle 10g version.
As one of the approaches it can be done as follows. Here we are using dense_rank() over() analytic function to assign a rank for a row in a ordered by rank group of rows .
select t.item_id
, t.supp_id
, t.rank
from (select item_id
, supp_id
, rank
, dense_rank() over(partition by item_id
order by rank) as rn
from t1
where item_id = 2
) t
where t.rn <= 2
Result:
ITEM_ID SUPP_ID RANK
---------- ---------- ----------
2 25 3
2 60 3
2 67 4
2 34 4
SQLFiddle Demo

help required with Sql query

I have a problem , got three Tables
Table A
ID Employee
1 1
2 2
3 3
Table B
Id EMployee HoursWorked HoursCode
1 1 10 Basic Hours
2 1 20 Holiday Pay
3 2 10 Basic hours
4 2 15 OverTime
Table C
ID Employee Payments PayCode
1 1 100 Bonus
2 2 150 Bonus
3 2 250 Student Loan
I want to get the records out of these table in minimum lines , so i can have one line which says
id Employee Hour HoursCode Payments PayCode
1 1 10 Basic Hours 100 Bonus
2 1 20 Holiday Pay null null
3 2 10 basic hours 150 Bonus
4 2 15 OverTime 250 Student loan
I have spent ages trying to get it ... But dont get the Null in the 2nd line it comes out with 100 Bonus in second line for employee 1
is there way i can do this Please Help
WITH bn AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY employee ORDER BY id) AS rn
FROM b
),
cn AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY employee ORDER BY id) AS rn
FROM c
)
SELECT *
FROM bn
FULL JOIN
cn
ON bn.employee = cn.employee
AND bn.rn = cn.rn
ORDER BY
COALESCE(bn.employee, cn.employee), COALESCE(bn.rn, cn.rn)