MySQL-Abfrage findet Werte in einer durch Kommas getrennten Zeichenfolge

90

Ich habe ein Feld COLORS (varchar(50))in meiner Tabelle SHIRTS, das eine durch Kommas getrennte Zeichenfolge enthält, wie z 1,2,5,12,15,. Jede Zahl repräsentiert die verfügbaren Farben.

Wenn select * from shirts where colors like '%1%'ich die Abfrage ausführe , um alle roten Hemden (Farbe = 1) zu erhalten, erhalte ich auch die Hemden, deren Farbe grau (= 12) und orange (= 15) ist.

Wie soll ich die Abfrage so umschreiben, dass NUR die Farbe 1 und nicht alle Farben mit der Nummer 1 ausgewählt werden?

Bikey77
quelle
6
Sie könnten dies über Regex tun, aber die viel bessere Lösung wäre, die Hemdfarben in eine separate Tabelle (Farben) aufzuteilen und eine Verknüpfungstabelle (shirt_colors) zu verwenden, indem Sie die IDs von color / shirt verwenden, um sie zu verknüpfen.
Ceejayoz
Ich kann nicht glauben, dass mit 6 Antworten keiner von ihnen den SET-Datentyp von MySQL erwähnt hat.
ColinM
1
Überprüfen Sie dies: stackoverflow.com/questions/12559876/…
Alireza

Antworten:

185

Der klassische Weg wäre, links und rechts Kommas hinzuzufügen:

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

Find_in_set funktioniert aber auch:

select * from shirts where find_in_set('1',colors) <> 0
Andomar
quelle
Ich habe find_in_set ausprobiert, aber es gibt das gleiche Ergebnis zurück, unabhängig vom eingegebenen Farbwert ... Irgendwelche Vorschläge?
Bikey77
@ bicyy77: Vielleicht ist dies das Problem, heißt es in der Dokumentation : Diese Funktion funktioniert nicht richtig, wenn das erste Argument ein Komma (",") enthält.
Andomar
Mein Unrecht, es war ein logischer Fehler aufgrund der gleichen Dummy-Werte. Es funktioniert gut. Vielen Dank!
Bikey77
@Andomar Bevor ich Ihre Antwort fand, habe ich mit IN zu kämpfen, aber Ihre ist Arbeit wie ein Zauber ... Vielen Dank ..
PHP Mentor
2
Es hat einen Einfluss auf die Leistung, da Find_in_set keinen Index verwendet
Kamran Shahid
30

FIND_IN_SET ist in diesem Fall dein Freund

select * from shirts where FIND_IN_SET(1,colors) 
Shakti Singh
quelle
4
find_in_set ist zu langsam für große Tabellen
Jeff_Alieffson
23

Schauen Sie sich die Funktion FIND_IN_SET für MySQL an.

SELECT * 
    FROM shirts 
    WHERE FIND_IN_SET('1',colors) > 0
Joe Stefanelli
quelle
1
Achtung: find in set verwendet keine Indizes für die Tabelle.
Edigu
11

Das wird sicher funktionieren und ich habe es tatsächlich ausprobiert:

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)

Versuche es !!!

RolandoMySQLDBA
quelle
Hey @rolandomysqldba, ich teste deine Anfrage und es funktioniert in Ordnung, aber ich muss einige Änderungen daran vornehmen. Angenommen, ich möchte alle Shirts mit einem Farbwert von 1,2 in der Spalte erhalten.
Ahsan Saeed
6

Wenn der Satz von Farben mehr oder weniger fest ist, besteht die effizienteste und auch am besten lesbare Möglichkeit darin, Zeichenfolgenkonstanten in Ihrer App zu verwenden und dann den MySQL- SETTyp mit FIND_IN_SET('red',colors)in Ihren Abfragen zu verwenden. Bei Verwendung des SETTyps mit FIND_IN_SET verwendet MySQL eine Ganzzahl zum Speichern aller Werte und verwendet Binärwerte"and" , um das Vorhandensein von Werten zu überprüfen. ist weitaus effizienter als das Scannen einer durch Kommas getrennten Zeichenfolge.

In SET('red','blue','green'), 'red'würde intern als gespeichert 1, 'blue'würde intern als gespeichert 2und 'green'würde intern als gespeichert 4. Der Wert 'red,blue'würde als 3( 1|2) und 'red,green'als 5( 1|4) gespeichert .

ColinM
quelle
3

Wenn Sie MySQL verwenden, gibt es eine Methode REGEXP, die Sie verwenden können ...

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

Dann würden Sie also verwenden:

SELECT * FROM `shirts` WHERE `colors` REGEXP '\b1\b'
KOGI
quelle
Konnte das nicht herausfinden, obwohl ich mir ziemlich sicher bin, dass es meine Schuld ist. Vielen Dank, ein Freund.
Bikey77
3

Sie sollten Ihr Datenbankschema tatsächlich so korrigieren, dass Sie drei Tabellen haben:

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

Wenn Sie dann alle roten Shirts finden möchten, führen Sie eine Abfrage wie folgt durch:

SELECT *
FROM shirt, color
WHERE color.color_name = 'red'
  AND shirt.shirt_id = shirtcolor.shirt_id
  AND color.color_id = shirtcolor.color_id
CanSpice
quelle
8
@Blindy: Das stimmt nur, wenn Sie davon ausgehen, dass das OP über Bearbeitungsrechte für das Datenbankschema verfügt. hat die Zeit, die Datenbank neu zu gestalten, die Daten zu migrieren und alle Clients neu zu gestalten; und dass die Verringerung der Komplexität für diese Abfrage die Zunahme der Komplexität im Rest der Anwendung überwiegt.
Andomar
1
@Andomar, dann wieder, wenn er auf Größenbeschränkungen für das Abrufen von Zeilen stößt und seine "Datensätze" abgeschnitten werden, dann beginnt der wahre Spaß!
Blindy
3
@Blindy: Du verpasst den Punkt; Ich behaupte nicht, dass er die beste Lösung hat, nur dass nicht jeder die Freiheit hat, seine Umgebung nach seinen Wünschen neu zu gestalten
Andomar
Ich stimme @Andomar
Adam B
2
select * from shirts where find_in_set('1',colors) <> 0

Funktioniert bei mir

Deepak Bhatta
quelle
0

1. Für MySQL:

SELECT FIND_IN_SET(5, columnname) AS result 
FROM table

2.Für Postgres SQL:

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

Beispiel

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

Sie können dies erreichen, indem Sie die folgende Funktion ausführen.

Führen Sie die folgende Abfrage aus, um eine Funktion zu erstellen.

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

Und rufen Sie diese Funktion so auf

msyql> select TOTAL_OCCURANCE('A,B,C,A,D,X,B,AB', 'A');
Delickate
quelle
-7

Alle Antworten sind nicht wirklich richtig. Versuchen Sie Folgendes:

select * from shirts where 1 IN (colors);
Backslider
quelle