Consulta de MySQL que busca valores en una cadena separada por comas


90

Tengo un campo COLORS (varchar(50))en mi tabla SHIRTSque contiene una cadena delimitada por comas como 1,2,5,12,15,. Cada número representa los colores disponibles.

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

¿Cómo debo reescribir la consulta para que seleccione SOLO el color 1 y no todos los colores que contienen el número 1?


6
Podría hacer esto a través de expresiones regulares, supongo, pero la solución mucho mejor sería dividir los colores de la camisa en una tabla separada (colores) y usar una tabla de unión (shirt_colors) usando los identificadores de color / camisa para vincularlos.
ceejayoz

No puedo creer que con 6 respuestas ninguno de ellos mencionó el tipo de datos SET de MySQL ..
ColinM

Respuestas:


185

La forma clásica sería agregar comas a la izquierda y a 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

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

@ bikey77: Tal vez este sea el problema, dice la documentación : Esta función no funciona correctamente si el primer argumento contiene un carácter de coma (“,”).
Andomar

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

@Andomar Antes de encontrar tu respuesta, no estaba luchando con IN, pero el tuyo funciona como un encanto ... Muchas gracias ..
Mentor de PHP

2
Tiene un impacto en el rendimiento ya que Find_in_set no usa el índice
Kamran Shahid


23

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

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

1
Cuidado: buscar en el conjunto no usa índices en la tabla.
edigu

11

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

lwdba@localhost (DB test) :: DROP TABLE IF EXISTS shirts;
Query OK, 0 rows affected (0.08 sec)

lwdba@localhost (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)

lwdba@localhost (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

lwdba@localhost (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)

Darle una oportunidad !!!


Hola @rolandomysqldba, pruebo tu consulta y funciona bien, pero necesito hacer algunos cambios. Digamos si quiero obtener todas las camisetas cuyo valor de color es 1,2 en la columna.
Ahsan Saeed

6

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

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



3

En realidad, debería arreglar el esquema de su base de datos para tener tres tablas:

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

Luego, si desea encontrar todas las camisetas que son rojas, 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 asume 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 complejidad en el resto de la aplicación.
Andomar

1
@Andomar, entonces nuevamente cuando se encuentre con restricciones de tamaño para la recuperación de filas y sus "registros" se recorten, ¡ESO es cuando comenzará la verdadera diversión!
Blindy

3
@Blindy: 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

Estoy de acuerdo con @Andomar
Adam B


0

1. Para MySQL:

SELECT FIND_IN_SET(5, columnname) AS result 
FROM table

2.Para Postgres SQL:

SELECT * 
FROM TABLENAME f
WHERE 'searchvalue' = ANY (string_to_array(COLUMNNAME, ','))

Ejemplo

select * 
from customer f
where '11' = ANY (string_to_array(customerids, ','))

0

Puede lograr esto siguiendo la función.

Ejecute la siguiente consulta para crear la 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 llama a esta función así

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

-7

Todas las respuestas no son realmente correctas, intente esto:

select * from shirts where 1 IN (colors);
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.