2012-05-07 27 views
24

Ich habe eine Spalte in einer meiner Tabelle, wo ich mehrere IDs getrennt durch Komma speichern. Gibt es eine Möglichkeit, wie ich den Wert dieser Spalte in der "IN" -Klausel einer Abfrage verwenden kann.Komma getrennte Werte in MySQL "IN" -Klausel

Die Säule (city) hat Werte wie 6,7,8,16,21,2

muss ich als

select * from table where e_ID in (Select city from locations where e_Id=?) 

ich mit Crozin Antwort zufrieden bin, aber ich bin offen für Vorschläge, Ansichten und Optionen.

Fühlen Sie sich frei, Ihre Ansichten zu teilen.

Antwort

27

Aufbauend auf dem FIND_IN_SET() Beispiel von @ Jeremy Smith, können Sie es mit einem Join tun, so dass Sie nicht über eine Unterabfrage ausgeführt werden.

SELECT * FROM table t 
JOIN locations l ON FIND_IN_SET(t.e_ID, l.city) > 0 
WHERE l.e_ID = ? 

Diese sehr schlecht durchzuführen, ist bekannt, da es Tisch-Scans zu tun hat, jede Kombination von Zeilen in table und locations die FIND_IN_SET() Funktion für Auswertung. Es kann keinen Index verwenden, und es gibt keine Möglichkeit, ihn zu verbessern.

Ich weiß, Sie sagten, Sie versuchen, das Beste aus einem schlechten Datenbank-Design zu machen, aber Sie müssen verstehen, wie drastisch schlecht das ist.

Erläuterung: Angenommen, ich würde Sie bitten, alle in einem Telefonbuch nachzuschlagen, dessen erste, mittlere oder letzte Initiale "J."Es gibt keine Möglichkeit, die sortierte Reihenfolge des Buches in diesem Fall hilft, da Sie sowieso jede einzelne Seite scannen müssen.

Die LIKE Lösung von @ fthiella hat ein ähnliches Problem in Bezug auf die Leistung. Es kann nicht indiziert werden.

siehe auch meine Antwort auf Is storing a delimited list in a database column really that bad? für andere Gefahren dieser Art und Weise denormalized Daten zu speichern

Wenn Sie eine Zusatztabelle erstellen können einen Index zu speichern, müssen Sie die Standorte zu jedem Eintrag in der Städteliste zuordnen.:

CREATE TABLE location2city (
location INT, 
city INT, 
PRIMARY KEY (location, city) 
); 

Angenommen, Sie eine Lookup-Tabelle für alle möglichen Städte haben (nicht nur derjenigen, die in der table) Sie können die Ineffizienz einer Zeit tragen die Zuordnung zu produzieren:

INSERT INTO location2city (location, city) 
    SELECT l.e_ID, c.e_ID FROM cities c JOIN locations l 
    ON FIND_IN_SET(c.e_ID, l.city) > 0; 

Jetzt können Sie führen eine wesentlich effizientere Abfrage finden Einträge in Ihrer table:

SELECT * FROM location2city l 
JOIN table t ON t.e_ID = l.city 
WHERE l.e_ID = ?; 

Dies kann die Verwendung eines Index machen. Jetzt müssen Sie nur darauf achten, dass jede INSERT/UPDATE/DELETE der Zeilen in locations auch fügt die entsprechenden Mapping-Reihen in location2city.

+0

+1 für eine solche klare Erklärung –

+0

Ich habe die Option FIND_IN_SET verwendet. Dies ist die beste Antwort des hier verfügbaren. Anstelle von Joins und Unterabfragen passt dies am besten. Aber wenn es nicht sehr wichtig ist, versuchen Sie nicht, kommagetrennte Werte in einer Spalte zu speichern. Das passt nicht zum RDBMS. Ich habe das verwendet, weil die Tabelle und die Daten viel früher erstellt wurden und es viele Daten gab, die ich nicht durch Datenübertragung riskieren möchte. – Vijay

+0

@Vijay: Vielen Dank für Ihren Vorschlag und vielen Dank Bill. –

25

Aus der Sicht von MySQL speichern Sie nicht mehrere IDs getrennt durch Komma - Sie speichern einen Wert Wert, der die gleiche Bedeutung wie "Hallo Welt" oder "Ich mag Kuchen!" - d.h. es hat keine Bedeutung.

Sie müssen eine getrennte Tabelle erstellen, die zwei Objekte aus der Datenbank miteinander verbindet. Lesen Sie mehr über many-to-many oder one-to-many (abhängig von Ihren Anforderungen) Beziehungen in SQL-basierten Datenbanken.

+0

Vielen Dank für Ihren Vorschlag erstellen und akzeptieren, dass das Design schlecht ist, aber ich habe einige Einschränkungen, die ich nicht ändern kann. Gibt es eine Möglichkeit, dies zu realisieren .... –

+0

@Crozin, fragt er offensichtlich für dieses Problem zu umgehen .......... – Pacerier

5

IN nimmt Zeilen, so dass Komma getrennte Spalte für die Suche wird nicht tun, was Sie wollen, aber wenn Sie Daten wie diese ('1', '2', '3') dies funktioniert, aber Sie können Daten wie nicht speichern Dies in Ihrem Feld, was auch immer Sie in die Spalte einfügen, es wird das Ganze als String nehmen.

24

als vielmehr IN auf Ihrer Abfrage zu verwenden, verwenden FIND_IN_SET (docs):

SELECT * FROM table 
WHERE 0 < FIND_IN_SET(e_ID, (
      SELECT city FROM locations WHERE e_ID=?)) 

Die üblichen Warnungen über erste Form Normalisierung anwenden (die Datenbank nicht mehrere Werte in einer einzigen Spalte gespeichert werden soll), aber wenn Du bist fest damit, dann sollte die obige Aussage helfen.

+0

Vielen Dank für Ihre Antwort.Dies ist sehr hilfreich. Irgendwelche Licht auf Leistung? –

+2

Die Leistung wird relativ schlecht sein, da 'FIND_IN_SET' keine Indizes verwenden kann. Wenn Sie sich Sorgen um die Leistung machen (und dazu berechtigt sind), müssen Sie Ihre Spalte 'locations'.city' wirklich in eine andere Tabelle unter Verwendung von 1nf aufteilen, und Sie können dann Indizes verwenden, um Ihre Leistung zu verbessern. –

+1

Wie @ JeremySmyth sagt, Theorie speichert eine durch Kommas getrennte Liste von Zahlen in einer Spalte verletzt eine Grundregel des Datenbankentwurfs, die Atomizitätsregel (eine Zelle, ein Wert). Also wäre es besser, das neu zu gestalten. ABER, wenn die Einschränkungen dies nicht erlauben, gehen Sie mit 'FIND_IN_SET'. – inigomedina

21

Diese verwendet keine IN-Klausel, aber es sollte das tun, was Sie brauchen:

Select * 
from table 
where 
    CONCAT(',', (Select city from locations where e_Id=?), ',') 
    LIKE 
    CONCAT('%,', e_ID, ',%') 

aber Sie müssen sicherstellen, dass e_ID keine Kommas enthalten oder einen lustigen Charakter.

z.B.

CONCAT(',', '6,7,8,16,21,2', ',') returns ',6,7,8,16,21,2,' 

e_ID=1 --> ',6,7,8,16,21,2,' LIKE '%,1,%' ? FALSE 
e_ID=6 --> ',6,7,8,16,21,2,' LIKE '%,6,%' ? TRUE 
e_ID=21 --> ',6,7,8,16,21,2,' LIKE '%,21,%' ? TRUE 
e_ID=2 --> ',6,7,8,16,21,2,' LIKE '%,2,%' ? TRUE 
e_ID=3 --> ',6,7,8,16,21,2,' LIKE '%,3,%' ? FALSE 
etc. 
+0

+1 @fthiella die einfachste von allen macht die besten asnwers :) und ich mag Codebeispiele in Antworten und Fragen: D – bonCodigo

+0

ja ... Konvertieren Sie das Vergleichsfeld von einem int in eine Zeichenfolge, und verwenden Sie wie. Das funktioniert. Ich weiß, dass das Problem durch schlechtes DB-Design verursacht wird, aber leider müssen wir manchmal mit diesem schlechten DB-Design arbeiten, manchmal ohne genug Zeit, um es neu zu gestalten. –

7

dieses in für ..here String-Verkettung Orakel von wm_concat

select * from table where e_ID in (Select wm_concat(city) from locations where e_Id=?) 

ja i mit raheel shan einverstanden gemacht wird .. in Ordnung bringen diese „in“ Klausel müssen wir unter die Spalte in Reihe machen Code eins mache diesen Job.

select * from table where to_char(e_ID) 
in (
    select substr(city,instr(city,',',1,rownum)+1,instr(city,',',1,rownum+1)-instr(city,',',1,rownum)-1) from 
    (
    select ','||WM_CONCAT(city)||',' city,length(WM_CONCAT(city))-length(replace(WM_CONCAT(city),','))+1 CNT from locations where e_Id=?) TST 
    ,ALL_OBJECTS OBJ where TST.CNT>=rownum 
    ) ; 
6

sollten Sie

FIND_IN_SET Returns Position Wert in Folge von kommagetrennte Werte

mysql> SELECT FIND_IN_SET('b','a,b,c,d'); 
    -> 2 
6

Sie wissen nicht verwenden, wenn dies ist, was Sie erreichen möchten. Mit MySQL ist es Feature verketten Werte aus einer Gruppe GROUP_CONCAT

Sie so etwas wie dies versuchen:

select * from table where e_ID in (Select GROUP_CONCAT(city SEPARATOR ',') from locations where e_Id=?) 
5

Sie müssen „SPLIT“ die Stadt Spaltenwerte. Es wird wie:

SELECT * 
    FROM table 
WHERE e_ID IN (SELECT TO_NUMBER(
           SPLIT_STR(city /*string*/ 
              , ',' /*delimiter*/ 
              , 1 /*start_position*/ 
              ) 
           ) 
        FROM locations); 

Sie mehr über die MySQL split_str Funktion hier lesen: http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/

Außerdem habe ich hier die TO_NUMBER Funktion von Oracle verwendet. Bitte ersetzen Sie es durch eine korrekte MySQL-Funktion.

2

Sie können eine vorbereitete Anweisung dynamisch wie this

set @sql = concat('select * from city where city_id in (', 
        (select cities from location where location_id = 3), 
        ')'); 
prepare in_stmt from @sql; 
execute in_stmt; 
deallocate prepare in_stmt; 
Verwandte Themen