tengo una mesa Tiene dos columnas class_id y student . La columna estudiante es una lista de estudiantes. El tipo de datos de la columna de estudiante es varchar . Quiero escribir una consulta SQL que devuelva filas donde las columnas de los estudiantes son un subconjunto de una lista más grande como ["A", "B", "C", "D", "E", "F", "G"]
class_id student 1 ["A","B"] 2 ["G","K","E"] 3 ["A","B","I"] Para el ejemplo anterior, mi consulta debe devolver solo una fila con class_id 1 .
Esto es lo que tengo hasta ahora
select * from A where student in ("A", "B", "C", "D", "E", "F", "G")pero no funciona
Esto no responde a su pregunta, pero muestra cómo debería haberse hecho esto. Escribe tus datos como:
class student 1 A 1 B 2 G 2 K 2 E 3 A 3 B 3 IAhora, puede responder preguntas como "¿qué clases está tomando A?"
SELECT UNIQUEROW class FROM A WHERE student='A'y "¿qué clases tienen A y B juntos?"
SELECT class, COUNT(*) AS count FROM A WHERE student IN ('A','B') GROUP BY class HAVING count = 2;No repetiré las sugerencias de diseño anteriores (pero preste atención a ellas). Todavía podemos procesar sus datos tal como están, con un poco de esfuerzo. Aquí hay un ejemplo usando etiquetas en lugar de nombres:
Ejemplo de trabajo:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=84641f571cb8e0bd2cb531f5c4ec586d
La mesa:
CREATE TABLE pivot ( id int AUTO_INCREMENT PRIMARY KEY , tags varchar(255) );Los datos de prueba:
INSERT INTO pivot (tags) VALUES ('tag1,tag2,tag3,tag1') , ('tag2') , ('tag4,tag5,tag4,tag4') , ('tag5,tag5,tag4') , ('tag5,tag5,tag4') , ('tag1,tag3,tag2,tag1') ;Aquí hay una manera de normalizar los datos dinámicamente:
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 ;Resultado:
+----+---------------------+------+ | 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 | +----+---------------------+------+Dada la lista normalizada anterior, podemos encontrar si algún conjunto dado es un subconjunto de un conjunto almacenado:
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 ;Resultado:
+----+---------------------+ | id | tags | +----+---------------------+ | 1 | tag1,tag2,tag3,tag1 | | 6 | tag1,tag3,tag2,tag1 | +----+---------------------+ 2 rows in set (0.003 sec)Dada la lista normalizada anterior, podemos encontrar si algún conjunto dado es un superconjunto (o coincidencia) de un conjunto almacenado (que responde a su pregunta específica):
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 ;Resultado:
+----+---------------------+ | id | tags | +----+---------------------+ | 2 | tag2 | +----+---------------------+ 1 row in set (0.002 sec)Con tus datos:
INSERT INTO pivot (tags) VALUES ('A,B') , ('G,K,E') , ('A,B,I') ;Solución:
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 ;Resultado:
+----+------+ | id | tags | +----+------+ | 1 | A,B | +----+------+ 1 row in set (0.003 sec)Su requisito se puede lograr si está utilizando MySQL 8+ / MariabDB 10.6
JSON_TABLE(expr, ruta COLUMNAS (column_list) [AS] alias)
Extrae datos de un documento JSON y los devuelve como una tabla relacional con las columnas especificadas.
Vamos a crear una tabla de muestra con datos.
CREATE TABLE `table1` ( `class_id` int(11) NOT NULL AUTO_INCREMENT, `student` longtext NOT NULL, PRIMARY KEY (`class_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `table1`(`student`) VALUES ('["A","B"]'), ('["G","K","E"]'), ('["A","B","I"]'); Para lograr su requisito, select * from A where student in ("A", "B", "C", "D", "E", "F", "G") , podemos usar JSON_TABLE
SELECT DISTINCT(class_id), student FROM table1, JSON_TABLE( student, "$[*]" COLUMNS( VALUE TEXT PATH "$" ) ) DATA WHERE DATA .Value IN("A", "B", "C", "D", "E", "F", "G")El resultado será
| identificador de clase | estudiante |
|---|---|
| 1 | ["A","B"] |
| 2 | ["G","K","E"] |
| 3 | ["A","B","I"] |
db<>violín aquí