I won't repeat the design suggestions above (but please pay attention to them).
We can still process your data as is, given a little effort. Here's an example using tags instead of names:
Working example:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=84641f571cb8e0bd2cb531f5c4ec586d
The table:
CREATE TABLE pivot (
id int AUTO_INCREMENT PRIMARY KEY
, tags varchar(255)
);
The test data:
INSERT INTO pivot (tags) VALUES
('tag1,tag2,tag3,tag1')
, ('tag2')
, ('tag4,tag5,tag4,tag4')
, ('tag5,tag5,tag4')
, ('tag5,tag5,tag4')
, ('tag1,tag3,tag2,tag1')
;
Here's a way to normalize the data dynamically:
WITH RECURSIVE seq (n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM seq WHERE n <= 9
)
SELECT DISTINCT t1.*
, REPLACE(TRIM(LEADING SUBSTRING_INDEX(t1.tags,',',seq.n-1) FROM SUBSTRING_INDEX(t1.tags,',',seq.n)), ',','') AS tag
FROM pivot AS t1
JOIN seq
ON seq.n > 0 AND SUBSTRING_INDEX(t1.tags,',',seq.n-1) <> SUBSTRING_INDEX(t1.tags,',',seq.n)
ORDER BY id, tag
;
Result:
+----+---------------------+------+
| id | tags | tag |
+----+---------------------+------+
| 1 | tag1,tag2,tag3,tag1 | tag1 |
| 1 | tag1,tag2,tag3,tag1 | tag2 |
| 1 | tag1,tag2,tag3,tag1 | tag3 |
| 2 | tag2 | tag2 |
| 3 | tag4,tag5,tag4,tag4 | tag4 |
| 3 | tag4,tag5,tag4,tag4 | tag5 |
| 4 | tag5,tag5,tag4 | tag4 |
| 4 | tag5,tag5,tag4 | tag5 |
| 5 | tag5,tag5,tag4 | tag4 |
| 5 | tag5,tag5,tag4 | tag5 |
| 6 | tag1,tag3,tag2,tag1 | tag1 |
| 6 | tag1,tag3,tag2,tag1 | tag2 |
| 6 | tag1,tag3,tag2,tag1 | tag3 |
+----+---------------------+------+
Given the above normalized list, we can then find if some given set is a subset of a stored set:
WITH RECURSIVE seq (n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM seq WHERE n <= 9
)
, norm AS (
SELECT DISTINCT t1.*
, REPLACE(TRIM(LEADING SUBSTRING_INDEX(t1.tags,',',seq.n-1) FROM SUBSTRING_INDEX(t1.tags,',',seq.n)), ',','') AS tag
FROM pivot AS t1
JOIN seq
ON seq.n > 0 AND SUBSTRING_INDEX(t1.tags,',',seq.n-1) <> SUBSTRING_INDEX(t1.tags,',',seq.n)
)
SELECT id
, tags
FROM norm
WHERE tag IN ('tag2', 'tag3')
GROUP BY id
HAVING COUNT(DISTINCT tag) = 2
ORDER BY id, tags
;
Result:
+----+---------------------+
| id | tags |
+----+---------------------+
| 1 | tag1,tag2,tag3,tag1 |
| 6 | tag1,tag3,tag2,tag1 |
+----+---------------------+
2 rows in set (0.003 sec)
Given the above normalized list, we can then find if some given set is a superset (or match) of a stored set (which addresses your specific question):
WITH RECURSIVE seq (n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM seq WHERE n <= 9
)
, norm AS (
SELECT DISTINCT t1.*
, REPLACE(TRIM(LEADING SUBSTRING_INDEX(t1.tags,',',seq.n-1) FROM SUBSTRING_INDEX(t1.tags,',',seq.n)), ',','') AS tag
FROM pivot AS t1
JOIN seq
ON seq.n > 0 AND SUBSTRING_INDEX(t1.tags,',',seq.n-1) <> SUBSTRING_INDEX(t1.tags,',',seq.n)
)
SELECT id
, tags
FROM norm
GROUP BY id
HAVING COUNT(DISTINCT CASE WHEN tag IN ('tag2', 'tag3', 'tag8', 'tag9') THEN tag END) = COUNT(DISTINCT tag)
ORDER BY id, tags
;
Result:
+----+---------------------+
| id | tags |
+----+---------------------+
| 2 | tag2 |
+----+---------------------+
1 row in set (0.002 sec)
With your data:
INSERT INTO pivot (tags) VALUES
('A,B')
, ('G,K,E')
, ('A,B,I')
;
Solution:
WITH RECURSIVE seq (n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM seq WHERE n <= 9
)
, norm AS (
SELECT DISTINCT t1.*
, REPLACE(TRIM(LEADING SUBSTRING_INDEX(t1.tags,',',seq.n-1) FROM SUBSTRING_INDEX(t1.tags,',',seq.n)), ',','') AS tag
FROM pivot AS t1
JOIN seq
ON seq.n > 0 AND SUBSTRING_INDEX(t1.tags,',',seq.n-1) <> SUBSTRING_INDEX(t1.tags,',',seq.n)
)
SELECT id
, tags
FROM norm
GROUP BY id
HAVING COUNT(DISTINCT CASE WHEN tag IN ('A','B','C','D','E','F','G') THEN tag END) = COUNT(DISTINCT tag)
ORDER BY id, tags
;
Result:
+----+------+
| id | tags |
+----+------+
| 1 | A,B |
+----+------+
1 row in set (0.003 sec)