I just had to solve a more general case of this problem, but in SQL server. The principles are probably similar though.
SetX
|-- Child1
|-- Child2
|-- Child4
SetY
|-- Child1
|-- Child3
ParentA -- has the children defined by SetX
|-- Child1
|-- Child2
|-- Child4
ParentB -- has the children defined by SetY
|-- Child1
|-- Child3
ParentC -- does not match any of the sets
|-- Child1
|-- Child2
|-- Child3
|-- Child4
M problem was around the users of a system (the parents), which roles they were assigned to within the system (the children), and which job description would fit the user (the sets).
The way I solved it was using a bit mask. Each child is assigned a unique 2^n bitmask. Membership of the set is then simply that the user's bitmask sum equals the bitmask sum of the set.
When there are lots of children and the bitmask is in danger of overflowing, you can use bigint bitmasks or multiple bitmasks (making sure to set the lower-order bitmasks to zero).
Here's an example written in T-SQL - pretty sure it would be simple to translate into MySQL (and I'm happy if someone wants to do that in their own answer).
declare @users table (
name varchar(10)
)
declare @skills table (
name varchar(20)
, id int identity (0, 1)
, bitmask bigint
)
declare @usersWithSkills table (
userName varchar(10)
, skillName varchar(20)
)
declare @groups table (
name varchar(20)
, bitmask bigint
)
declare @skillsInGroups table (
groupName varchar(10)
, skillName varchar(20)
)
insert @users (name)
values ('Pat')
, ('Oprah')
, ('Millie')
, ('Bert')
insert @skills (name)
values ('Latin')
, ('Icelandic')
, ('Physics')
insert @groups (name)
values ('polyglot')
, ('modern')
, ('omniscient')
insert @skillsInGroups (groupName, skillName)
values ('polyglot', 'Latin')
, ('polyglot', 'Icelandic')
, ('modern', 'Physics')
, ('modern', 'Icelandic')
, ('omniscient', 'Latin')
, ('omniscient', 'Icelandic')
, ('omniscient', 'Physics')
insert @usersWithSkills (userName, skillName)
values ('Pat', 'Latin')
, ('Pat', 'Icelandic')
, ('Oprah', 'Latin')
, ('Oprah', 'Icelandic')
, ('Oprah', 'Physics')
, ('Millie', 'Icelandic')
, ('Millie', 'Physics')
, ('Bert', 'Latin')
-- give each skill a bitmask value
update @skills
set bitmask = power(2, id)
-- set the total bitmask values for each group
update g1
set g1.bitmask = t.sum_ind
from @groups g1
inner join (
select g.name, sum_ind = sum(r.bitmask)
from @groups g
inner join @skillsInGroups rg
on rg.groupName = g.name
inner join @skills r
on r.name = rg.skillName
group by g.name
) t
on t.name = g1.name
select u1.userName, groupName = g.name
from (
select userName = u.name
, bitmask_total = sum(r.bitmask)
from @users u
inner join @usersWithSkills uir
on uir.userName = u.name
inner join @skills r
on r.name = uir.skillName
group by u.name
) u1
left join @groups g
on g.bitmask = u1.bitmask_total
The results I get from this are
userName groupName
---------- --------------------
Bert NULL
Millie modern
Oprah omniscient
Pat polyglot
(4 rows affected)