Requête MySQL recherche des valeurs dans une chaîne séparée par des virgules

90

J'ai un champ COLORS (varchar(50))dans ma table SHIRTSqui contient une chaîne délimitée par des virgules telle que1,2,5,12,15, . Chaque nombre représentant les couleurs disponibles.

Lors de l'exécution de la requête select * from shirts where colors like '%1%'pour obtenir toutes les chemises rouges (couleur = 1), j'obtiens également les chemises dont la couleur est le gris (= 12) et l'orange (= 15).

Comment dois-je réécrire la requête de manière à sélectionner UNIQUEMENT la couleur 1 et non toutes les couleurs contenant le numéro 1?

bikey77
la source
6
Vous pouvez le faire via regex, je suppose, mais la bien meilleure solution serait de diviser les couleurs de chemise dans une table séparée (couleurs) et d'utiliser une table de jointure (shirt_colors) en utilisant les identifiants de couleur / chemise pour les lier.
ceejayoz
Je ne peux pas croire avec 6 réponses, aucun d'entre eux n'a mentionné le type de données SET de MySQL ..
ColinM
1
vérifiez ceci: stackoverflow.com/questions/12559876/…
Alireza

Réponses:

185

La manière classique serait d'ajouter des virgules à gauche et à droite:

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

Mais find_in_set fonctionne également:

select * from shirts where find_in_set('1',colors) <> 0
Andomar
la source
J'ai essayé find_in_set mais il renvoie le même résultat quelle que soit la valeur de couleur que j'entre ... Des suggestions?
bikey77
@ bikey77: C'est peut-être le problème, dit la documentation : Cette fonction ne fonctionne pas correctement si le premier argument contient une virgule («,»).
Andomar
Mon tort, c'était une erreur logique due aux mêmes valeurs factices. Ça fonctionne bien. Merci!
bikey77
@Andomar Avant de trouver votre réponse, je me débattais avec IN mais le vôtre est un travail comme un charme ... Merci beaucoup ..
PHP Mentor
2
Cela a un impact sur les performances car Find_in_set n'utilise pas d'index
Kamran Shahid
30

FIND_IN_SET est votre ami dans ce cas

select * from shirts where FIND_IN_SET(1,colors) 
Shakti Singh
la source
4
find_in_set est trop lent pour les grandes tables
Jeff_Alieffson
23

Jetez un œil à la fonction FIND_IN_SET pour MySQL.

SELECT * 
    FROM shirts 
    WHERE FIND_IN_SET('1',colors) > 0
Joe Stefanelli
la source
1
Attention: find in set n'utilise pas d'index sur la table.
edigu
11

Cela fonctionnera à coup sûr, et je l'ai essayé:

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)

Essaie !!!

RolandoMySQLDBA
la source
Hey @rolandomysqldba, je teste votre requête et cela fonctionne bien mais je dois y apporter des modifications. Disons que si je veux obtenir toutes les chemises dont la valeur de couleur est 1,2 dans la colonne.
Ahsan Saeed le
6

Si l'ensemble de couleurs est plus ou moins fixe, le moyen le plus efficace et le plus lisible serait d'utiliser des constantes de chaîne dans votre application, puis d'utiliser le SETtype de MySQL avec FIND_IN_SET('red',colors)dans vos requêtes. Lors de l'utilisation du SETtype avec FIND_IN_SET , MySQL utilise un entier pour stocker toutes les valeurs et utilise une "and"opération binaire pour vérifier la présence de valeurs, ce qui est bien plus efficace que de scanner une chaîne séparée par des virgules.

Dans SET('red','blue','green'), 'red'serait stocké en interne en tant que 1, 'blue'serait stocké en interne en tant que 2et 'green'serait stocké en interne en tant que 4. La valeur 'red,blue'serait stockée sous la forme 3( 1|2) et 'red,green'sous la forme 5( 1|4).

ColinM
la source
3

Si vous utilisez MySQL, il existe une méthode REGEXP que vous pouvez utiliser ...

http://dev.mysql.com/doc/refman/5.1/en/regexp.html#operator_regexp

Alors vous utiliseriez:

SELECT * FROM `shirts` WHERE `colors` REGEXP '\b1\b'
KOGI
la source
Impossible de comprendre celui-ci, même si je suis presque sûr que c'est de ma faute. Merci un mil compagnon.
bikey77
3

Vous devez en fait corriger le schéma de votre base de données de sorte que vous ayez trois tables:

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

Ensuite, si vous voulez trouver toutes les chemises rouges, vous feriez une requête comme:

SELECT *
FROM shirt, color
WHERE color.color_name = 'red'
  AND shirt.shirt_id = shirtcolor.shirt_id
  AND color.color_id = shirtcolor.color_id
CanSpice
la source
8
@Blindy: Ce n'est vrai que si vous supposez que l'OP a des droits d'édition sur le schéma de la base de données; a le temps de repenser la base de données, de migrer les données et de refactoriser tous les clients; et que la réduction de la complexité de cette requête l'emporte sur l'augmentation de la complexité du reste de l'application.
Andomar
1
@Andomar, puis à nouveau quand il rencontrera des restrictions de taille pour les récupérations de lignes et que ses "enregistrements" seront coupés, CELA est là que le vrai plaisir commencera!
Blindy
3
@Blindy: Vous manquez le point; Je ne dis pas qu'il a la meilleure solution, juste que tout le monde n'a pas la liberté de repenser son environnement à son goût
Andomar
Je suis d'accord avec @Andomar
Adam B
3
select * from shirts where find_in_set('1',colors) <> 0

Travaille pour moi

Deepak Bhatta
la source
0

1. Pour MySQL:

SELECT FIND_IN_SET(5, columnname) AS result 
FROM table

2.Pour Postgres SQL:

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

Exemple

select * 
from customer f
where '11' = ANY (string_to_array(customerids, ','))
Saranga kapilarathna
la source
0

Vous pouvez y parvenir en suivant la fonction.

Exécutez la requête suivante pour créer la fonction.

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)        
);

Et appelez cette fonction comme ça

msyql> select TOTAL_OCCURANCE('A,B,C,A,D,X,B,AB', 'A');
Délicat
la source
-7

Toutes les réponses ne sont pas vraiment correctes, essayez ceci:

select * from shirts where 1 IN (colors);
Récidiviste
la source