2011-01-10 12 views
10

Esta pregunta se deriva de MYSQL join results set wiped results during IN() in where clause?dividir una cadena en MySQL del GROUP_CONCAT en un (array, como, una expresión, lista) que, en() puede comprender

Así, versión corta de la cuestión. ¿Cómo se puede convertir la cadena devuelta por GROUP_CONCAT en una lista de expresiones separadas por comas que IN() tratará como una lista de múltiples elementos para repetir?

N.B. Los documentos MySQL parecen referirse al "(coma, separado, listas)" usado por IN() como 'listas de expresiones', y curiosamente las páginas en IN() parecen ser más o menos las únicas páginas en los documentos MySQL para alguna vez consulte las listas de expresiones Así que no estoy seguro de si las funciones destinadas a crear matrices o tablas temporales serían útiles aquí.


versión basada en ejemplos largo de la pregunta: ¿A partir de una 2-mesa de DB como esto:

SELECT id, name, GROUP_CONCAT(tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id GROUP BY person.id; 
+----+------+----------------------+ 
| id | name | GROUP_CONCAT(tag_id) | 
+----+------+----------------------+ 
| 1 | Bob | 1,2     | 
| 2 | Jill | 2,3     | 
+----+------+----------------------+ 

¿Cómo puedo convertir este, que ya que utiliza una cadena se trata como equivalente lógico de (1 = X) y (2) = X ...

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id 
GROUP BY person.id HAVING ((1 IN (GROUP_CONCAT(tag.tag_id))) AND (2 IN (GROUP_CONCAT(tag.tag_id)))); 
Empty set (0.01 sec) 

... en algo donde el resultado GROUP_CONCAT se trata como una lista, de modo que para Bob, que sería equivalente a:

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id AND person.id = 1 
GROUP BY person.id HAVING ((1 IN (1,2)) AND (2 IN (1,2))); 
+------+--------------------------+ 
| name | GROUP_CONCAT(tag.tag_id) | 
+------+--------------------------+ 
| Bob | 1,2      | 
+------+--------------------------+ 
1 row in set (0.00 sec) 

... y para Jill, sería equivalente a:

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id AND person.id = 2 
GROUP BY person.id HAVING ((1 IN (2,3)) AND (2 IN (2,3))); 
Empty set (0.00 sec) 

... por lo que el resultado global sería una cláusula de búsqueda exclusiva que requiere todas las etiquetas que figuran que no utiliza HAVING COUNT (DISTINTO ...) ?

(nota: Esta lógica funciona sin la Y, aplicando al primer carácter de la cadena, por ejemplo

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id 
    GROUP BY person.id HAVING ((2 IN (GROUP_CONCAT(tag.tag_id)))); 
+------+--------------------------+ 
| name | GROUP_CONCAT(tag.tag_id) | 
+------+--------------------------+ 
| Jill | 2,3      | 
+------+--------------------------+ 
1 row in set (0.00 sec) 
+1

Esto es agradable, pero vale la pena señalar que no es necesario hacer el 'GROUP_CONCAT()' 'en todo el FIND_IN_SET()'. Puedes simplemente 'SELECCIONAR GROUP_CONCAT (tag.tag_id) AS tags_list' y luego' HAVING FIND_IN_SET (20, tags_list) ' – Treffynnon

Respuesta

33

En lugar de utilizar IN(), sería el uso de FIND_IN_SET() ser una opción demasiado

http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set

.?
mysql> SELECT FIND_IN_SET('b','a,b,c,d'); 
    -> 2 

Aquí es un ejemplo completo basado en el problema de ejemplo en la pregunta, confirmado como probado por el autor de la pregunta en una edición anterior a la pregunta: ¿

SELECT name FROM person LEFT JOIN tag ON person.id = tag.person_id GROUP BY person.id 
    HAVING (FIND_IN_SET(1, GROUP_CONCAT(tag.tag_id))) AND (FIND_IN_SET(2, GROUP_CONCAT(tag.tag_id))); 
+------+ 
| name | 
+------+ 
| Bob | 
+------+ 
+0

¡Gracias! Necesito esto, también. –

+0

¡Perfecto! Un consejo: no olvide que dado que es como una llamada a función, debe haber ** espacio libre antes del paréntesis de apertura/paréntesis, ** de lo contrario, obtendrá un error 'Función nombrebd.FIND_IN_SET no existe'. – user568458

+0

Bueno, esta es una solución, pero he encontrado que FIND_IN_SET es extremadamente menos eficiente que IN es ... sé si hay alguna manera de realmente lograr lo que dice la pregunta, una forma de dividir una cadena en una matriz ¿variable? –

4

Puede pasar una cadena como matriz, utilizando un separador de división y explotar en una función que funcionará con los resultados.

Para un ejemplo trivial, si usted tiene una matriz de cadenas como esto: 'uno | dos | árbol | cuatro | cinco', y quiere saber si dos es en la matriz, puede hacerlo de esta manera:

create function str_in_array(split_index varchar(10), arr_str varchar(200), compares varchar(20)) 
    returns boolean 
    begin 
    declare resp boolean default 0; 
    declare arr_data varchar(20); 

    -- While the string is not empty 
    while(length(arr_str) > 0 ) do 

    -- if the split index is in the string 
    if(locate(split_index, arr_str)) then 

     -- get the last data in the string 
    set arr_data = (select substring_index(arr_str, split_index, -1)); 

    -- remove the last data in the string 
    set arr_str = (select 
     replace(arr_str, 
     concat(split_index, 
      substring_index(arr_str, split_index, -1) 
     ) 
     ,'') 
    ); 
    -- if the split index is not in the string 
    else 
    -- get the unique data in the string 
    set arr_data = arr_str; 
    -- empties the string 
    set arr_str = ''; 
    end if; 

    -- in this trivial example, it returns if a string is in the array 
    if arr_data = compares then 
    set resp = 1; 
    end if; 

end while; 

return resp; 
end 
| 

delimiter ; 

Quiero crear un conjunto de funciones útiles de mysql para trabajar con este método. Cualquier persona interesada por favor contáctame.

Para más ejemplos, visite http://blog.idealmind.com.br/mysql/how-to-use-string-as-array-in-mysql-and-work-with/