2011-02-17 25 views
54

Tengo un campo COLORS (varchar(50)) en una tabla SHIRTS que contiene una cadena delimitada por comas como 1,2,5,12,15,. Cada número representa los colores disponibles.Búsqueda de MySQL valores en una cadena separada por comas

Al ejecutar la consulta select * from shirts where colors like '%1%' para obtener todas las camisetas rojas (color = 1), también obtengo las camisas cuyo color es gris (= 12) y naranja (= 15).

¿Cómo debo volver a escribir la consulta para que se seleccione SOLAMENTE el color 1 y no todos los colores que contienen el número 1?

+5

Puede hacer esto a través de Regex, supongo, pero la mejor solución sería romper los colores de la camisa en una mesa separada (colores) y usar una mesa de combinación (shirt_colors) usando los identificadores de color/camisa para unirlos. – ceejayoz

+0

No puedo creer con 6 respuestas * ninguna * mencionó el tipo de datos SET de MySQL. – ColinM

+0

verifique esto: http://stackoverflow.com/questions/12559876/finding-exact-value-from-a-comma -separated-string-in-php-mysql – Alireza

Respuesta

125

la manera clásica sería añadir la coma hacia la izquierda y la derecha:

select * from shirts where CONCAT(',', colors, ',') like '%,1,%' 

Pero find_in_set también funciona:

select * from shirts where find_in_set('1',colors) <> 0 
+0

Intenté find_in_set pero devuelve el mismo resultado sin importar el valor de color que ingrese ... ¿Alguna sugerencia? – bikey77

+0

@ bikey77: Tal vez este es el problema, la [documentación] (http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set) dice: Esta función no no funciona correctamente si el primer argumento contiene un carácter de coma (","). – Andomar

+0

Mi error, fue un error lógico debido a los mismos valores ficticios. Funciona bien. ¡Gracias! – bikey77

20

Tome un vistazo a la FIND_IN_SET función para MySQL.

SELECT * 
    FROM shirts 
    WHERE FIND_IN_SET('1',colors) > 0 
+0

Cuidado: encontrar en el conjunto no usa índices en la tabla. – edigu

3

En realidad debería arreglar el esquema de base de modo que tiene tres tablas:

shirt: shirt_id, shirt_name 
color: color_id, color_name 
shirtcolor: shirt_id, color_id 

Entonces, si usted quiere encontrar todas las camisas que son de color rojo, que haría una consulta como:

SELECT * 
FROM shirt, color 
WHERE color.color_name = 'red' 
    AND shirt.shirt_id = shirtcolor.shirt_id 
    AND color.color_id = shirtcolor.color_id 
+8

@Blindy: Eso solo es cierto si supone que el OP tiene derechos de edición en el esquema de la base de datos; tiene tiempo para rediseñar la base de datos, migrar los datos y refactorizar todos los clientes; y que la reducción de la complejidad de esta consulta supera el aumento de la complejidad en el resto de la aplicación. – Andomar

+1

@Andomar, y luego otra vez cuando se encuentre con restricciones de tamaño para la recuperación de filas y sus "registros" serán recortados, ¡ahí es cuando comenzará la verdadera diversión! – Blindy

+3

@Blindy: Te estás perdiendo el punto; No estoy argumentando que él tiene la mejor solución, solo que no todos tienen la libertad de rediseñar su entorno a su gusto – Andomar

20

FIND_IN_SET es su amigo en este caso

select * from shirts where FIND_IN_SET(1,colors) 
+0

find_in_set es demasiado lento para tablas grandes –

8

Esto funcionará con seguridad, y de hecho lo probé:

[email protected] (DB test) :: DROP TABLE IF EXISTS shirts; 
Query OK, 0 rows affected (0.08 sec) 

[email protected] (DB test) :: CREATE TABLE shirts 
    -> (<BR> 
    -> id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    -> ticketnumber INT, 
    -> colors VARCHAR(30) 
    ->);<BR> 
Query OK, 0 rows affected (0.19 sec) 

[email protected] (DB test) :: INSERT INTO shirts (ticketnumber,colors) VALUES 
    -> (32423,'1,2,5,12,15'), 
    -> (32424,'1,5,12,15,30'), 
    -> (32425,'2,5,11,15,28'), 
    -> (32426,'1,2,7,12,15'), 
    -> (32427,'2,4,8,12,15'); 
Query OK, 5 rows affected (0.06 sec) 
Records: 5 Duplicates: 0 Warnings: 0 

[email protected] (DB test) :: SELECT * FROM shirts WHERE LOCATE(CONCAT(',', 1 ,','),CONCAT(',',colors,',')) > 0; 
+----+--------------+--------------+ 
| id | ticketnumber | colors  | 
+----+--------------+--------------+ 
| 1 |  32423 | 1,2,5,12,15 | 
| 2 |  32424 | 1,5,12,15,30 | 
| 4 |  32426 | 1,2,7,12,15 | 
+----+--------------+--------------+ 
3 rows in set (0.00 sec) 

Dar Es un intento !!!

+0

¡Esto funciona! ¡Gracias! – bikey77

+0

¡Gracias trabajado para mí! :) –

6

Si el conjunto de colores es más o menos fijo, la forma más eficiente y más legible sería usar constantes de cadena en su aplicación y luego utilizar el tipo SET de MySQL con FIND_IN_SET('red',colors) en sus consultas. Cuando se usa el tipo SET con FIND_IN_SET, MySQL usa un entero para almacenar todos los valores y usa la operación binaria "and" para verificar la presencia de valores que es mucho más eficiente que escanear una cadena separada por comas.

En SET('red','blue','green'), 'red' serían almacena internamente como 1, 'blue' serían almacena internamente como 2 y 'green' serían almacena internamente como 4. El valor 'red,blue' se almacenará como 3 (1|2) y 'red,green' como 5 (1|4).

-4

Todas las respuestas no son realmente correcto, intente esto:

select * from shirts where 1 IN (colors); 
0
select * from shirts where find_in_set('1',colors) <> 0 

Obras para mí

0

se puede lograr esto mediante la siguiente función.

Ejecute la siguiente consulta para crear una función.

DELIMITER || 
CREATE FUNCTION `TOTAL_OCCURANCE`(`commastring` TEXT, `findme`  VARCHAR(255)) RETURNS int(11) 
NO SQL 
-- SANI: First param is for comma separated string and 2nd for string to find. 
return ROUND ( 
    (
     LENGTH(commastring) 
     - LENGTH(REPLACE (commastring, findme, "")) 
    )/LENGTH(findme)   
); 

Y llamar a esta función como esta

msyql> select TOTAL_OCCURANCE('A,B,C,A,D,X,B,AB', 'A'); 

espero que me ayudaría.

Cuestiones relacionadas