Reputation: 106
There are 2 tables:
Employee
(id_employee, worker_name)
Groups
(id_employee, group_name)
Here is creation script for you:
CREATE TABLE Employee (
id_employee int identity(1,1) NOT NULL CONSTRAINT PK_Employee PRIMARY KEY CLUSTERED,
worker_name nvarchar(100) CONSTRAINT UQ_Employee_worker_name UNIQUE
);
CREATE TABLE Groups (
id_employee int NOT NULL CONSTRAINT FK_Groups_id_employee FOREIGN KEY REFERENCES Employee (id_employee),
group_name varchar(10) NOT NULL,
CONSTRAINT PK_Groups PRIMARY KEY CLUSTERED (group_name, id_employee)
);
INSERT Employee
SELECT 'worker 1'
UNION ALL SELECT 'worker 2'
UNION ALL SELECT 'worker 3'
UNION ALL SELECT 'worker 4';
INSERT Groups
SELECT 1, 'a1'
UNION ALL SELECT 2, 'a1'
UNION ALL SELECT 3, 'a2'
UNION ALL SELECT 4, 'a2'
UNION ALL SELECT 1, 'b1'
UNION ALL SELECT 2, 'b1'
UNION ALL SELECT 3, 'b2'
UNION ALL SELECT 4, 'b2'
UNION ALL SELECT 2, 'b3'
UNION ALL SELECT 3, 'b3'
UNION ALL SELECT 4, 'b3';
I need a query that for a given id_group
will return all other groups that have exactly the same employees.
For example:
SELECT for 'a1'
-> Should return 'b1' because in both groups there are: worker 1 & worker 2
SELECT for 'a2'
-> Should return 'b2' because in both groups there are: worker 3 & worker 4
Note that groups need to be exactly the same, all members of a1
need to be in b1
and size of both groups need to be the same as well.
Upvotes: 2
Views: 251
Reputation: 13506
Here is the sql query to get the desired result.
DECLARE @group_name varchar(10)='a1'
;WITH CTE(group_name,emp_ids) as (select group_name,(SELECT cast(id_employee as varchar(10))+ ', ' as [text()]
FROM Groups where group_name=g.group_name
ORDER BY id_employee DESC
FOR XML PATH('') ) as emp_ids from Groups g group by group_name)
SELECT group_name FROM CTE where emp_ids in(select emp_ids from CTE where group_name = @group_name)
and group_name <> @group_name
Upvotes: 0
Reputation: 50241
This works. See the setup script I added to the question if you want to try it.
SELECT DISTINCT G.group_name
FROM Groups G
WHERE
G.group_name <> @group
AND NOT EXISTS (
SELECT *
FROM
(SELECT * FROM Groups WHERE group_name = @group) G1
FULL JOIN (SELECT * FROM Groups WHERE G.group_name = group_name) G2
ON G1.id_employee = G2.id_employee
WHERE EXISTS (SELECT G1.id_employee EXCEPT SELECT G2.id_employee)
);
Upvotes: 0
Reputation: 17915
It took a few edits to get the one I wanted
with pairs as (
select gs1.group_name as group1, gs2.group_name as group2, gs1.emp_count as emp_count
from
(select group_name, count(1) as emp_count from groups group by group_name) as gs1 inner join
(select group_name, count(1) as emp_count from groups group by group_name) as gs2
/* choose parameterized */
--on gs1.group_name = 'a1' and gs1.group_name != gs2.group_name and gs1.emp_count = gs2.emp_count
/* or all pairs */
on gs1.group_name < gs2.group_name and gs1.emp_count = gs2.emp_count
)
select
pairs.group1, pairs.group2
from
pairs inner join
(select group_name, id_employee from Groups) as g1
on g1.group_name = pairs.group1 inner join
(select group_name, id_employee from Groups) as g2
on g2.group_name = pairs.group2 and g1.id_employee = g2.id_employee
group by
pairs.group1, pairs.group2
having
min(pairs.emp_count) = count(g1.group_name);
Upvotes: 0
Reputation: 23125
You can try this solution:
SELECT b.group_name
FROM
(
SELECT aa.id_employee, aa.group_name, bb.group_count
FROM groups aa
CROSS JOIN
(
SELECT COUNT(1) AS group_count FROM groups WHERE group_name = 'a1'
) bb
WHERE aa.group_name = 'a1'
) a
INNER JOIN groups b ON a.id_employee = b.id_employee AND a.group_name <> b.group_name
INNER JOIN
(
SELECT group_name, COUNT(1) AS other_group_count
FROM groups
GROUP BY group_name
) c ON b.group_name = c.group_name
WHERE a.group_count = c.other_group_count
GROUP BY b.group_name, a.group_count
HAVING COUNT(1) = a.group_count
Upvotes: 1
Reputation: 1269973
You can use SQL set operations to do the work. The idea is to match employees together within groups and to count the totals in the groups.
select others.group_name
from (select g.*, count(*) over (partition by group_name) as numemps
from groups g
where group_name <> @group
) others left outer join
(select g.*, count(*) over (partition by group_name) as numemps
from groups g
where group_name = @group
) thegroup
on others.id_employee = thegroup.id_employee and
others.numemps = thegroup.numemps
group by others.group_name
having count(*) = max(thegroup.numemps) and
max(case when thegroup.id_employee is null then 1 else 0 end) = 0
So, this query divides the world into two . . . your group and all the other groups. It then matches them by name and aggregates by groups. The only candidate groups are the ones with the same numbers of employees.
The having clause chooses the groups that match. This means that every name in the other group matches a name in the group you care about. Since the sizes of the groups are the same, and each name in your group is matched, the groups contain the same employees.
Upvotes: 2