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?


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

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

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


23

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

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

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 !!!


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 .



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

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


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, ','))

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

-7

Alle Antworten sind nicht wirklich richtig. Versuchen Sie Folgendes:

select * from shirts where 1 IN (colors);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.