FIND_IN_SET () vs IN ()


125

Ich habe 2 Tabellen in meiner Datenbank. Eine ist für Bestellungen und eine für Unternehmen.

Bestellungen haben diese Struktur:

OrderID     |     attachedCompanyIDs
------------------------------------
   1                     1,2,3
   2                     2,4

Und das Unternehmen hat diese Struktur:

CompanyID      |        name
--------------------------------------
    1                 Company 1
    2                 Another Company
    3                 StackOverflow
    4                 Nothing

Um die Firmennamen eines Auftrags zu erhalten, kann ich eine Abfrage als solche durchführen:

SELECT name FROM orders,company
WHERE orderID = 1 AND FIND_IN_SET(companyID, attachedCompanyIDs)

Diese Abfrage funktioniert einwandfrei, die folgende Abfrage jedoch nicht.

SELECT name FROM orders,company
WHERE orderID = 1 AND companyID IN (attachedCompanyIDs)

Warum funktioniert die erste Abfrage, aber nicht die zweite?

Die erste Abfrage gibt Folgendes zurück:

name
---------------
Company 1
Another Company
StackOverflow

Die zweite Abfrage gibt nur Folgendes zurück:

name
---------------
Company 1

Warum ist das so, warum gibt die erste Abfrage alle Unternehmen zurück, während die zweite Abfrage nur die erste zurückgibt?


3
AttachedCompanyIDs ist eine große Zeichenfolge, daher versucht
MySQL,

Ich denke, dies ist das beste Beispiel mysqltutorial.org/mysql-find_in_set
Shurvir Mori

Antworten:


100
SELECT  name
FROM    orders,company
WHERE   orderID = 1
        AND companyID IN (attachedCompanyIDs)

attachedCompanyIDsist ein Skalarwert, der in INT(Typ von companyID) umgewandelt wird.

Die Besetzung gibt nur Zahlen bis zur ersten Nicht-Ziffer zurück (in Ihrem Fall ein Komma).

So,

companyID IN ('1,2,3')  companyID IN (CAST('1,2,3' AS INT))  companyID IN (1)

In PostgreSQLkönnen Sie den String in ein Array umwandeln (oder ihn zunächst als Array speichern):

SELECT  name
FROM    orders
JOIN    company
ON      companyID = ANY (('{' | attachedCompanyIDs | '}')::INT[])
WHERE   orderID = 1

und dies würde sogar einen Index auf verwenden companyID.

Leider funktioniert dies nicht, MySQLda letzteres keine Arrays unterstützt.

Sie finden diesen Artikel vielleicht interessant (siehe #2):

Aktualisieren:

Wenn die Anzahl der Werte in den durch Kommas getrennten Listen angemessen begrenzt ist (z. B. nicht mehr als 5), können Sie versuchen, diese Abfrage zu verwenden:

SELECT  name
FROM    orders
CROSS JOIN
        (
        SELECT  1 AS pos
        UNION ALL
        SELECT  2 AS pos
        UNION ALL
        SELECT  3 AS pos
        UNION ALL
        SELECT  4 AS pos
        UNION ALL
        SELECT  5 AS pos
        ) q
JOIN    company
ON      companyID = CAST(NULLIF(SUBSTRING_INDEX(attachedCompanyIDs, ',', -pos), SUBSTRING_INDEX(attachedCompanyIDs, ',', 1 - pos)) AS UNSIGNED)

3
Danke für die Erklärung. Ich wusste nicht, dass das angehängte CompanyIDs-Feld in ein INT umgewandelt wurde. Gibt es in MySQL einen Weg, dies zu umgehen? FIND_IN_SETfunktioniert, verwendet jedoch keine Indizes und ist möglicherweise langsam mit vielen Informationen in der Firmentabelle.
Rocket Hazmat

1
Können Sie das Update erklären? Was genau macht das, weil es zu funktionieren scheint.
Rocket Hazmat

1
@Rocket: Es entfernt posElemente vom Anfang des CVSund wandelt den Rest in eine Ganzzahl um.
Quassnoi

9
Daumen hoch (y) für10 things in MySQL (that won’t work as expected)
NullPointer

@ Quassnoi, warum schreibst du CROSS JOIN? Sind sie in MySQL nicht alle gleich ?
Pacerier

13

attachCompanyIDs ist eine große Zeichenfolge, daher versucht mysql, eine Firma in dieser Umwandlung in eine Ganzzahl zu finden

wenn Sie wo in verwenden

also wenn comapnyid = 1:

companyID IN ('1,2,3')

Dies ist return true

aber wenn die Nummer 1 nicht an erster Stelle steht

 companyID IN ('2,3,1')

seine Rückkehr falsch


3

Um den Namen aller verbundenen Unternehmen zu erhalten, nicht basierend auf einer bestimmten ID.

SELECT 
    (SELECT GROUP_CONCAT(cmp.cmpny_name) 
    FROM company cmp 
    WHERE FIND_IN_SET(cmp.CompanyID, odr.attachedCompanyIDs)
    ) AS COMPANIES
FROM orders odr

1

Da die zweite Abfrage nach Zeilen mit der ID 1 ODER 2 ODER 3 sucht, sucht die erste Abfrage nach einem der durch Kommas getrennten Werte, die in der Unternehmens-ID vorhanden sind.

und ein weiteres Problem hierbei ist, dass Sie die Tabellen nicht über einen gemeinsamen Schlüssel in Ihrem Speicherort verknüpfen, sodass Sie eine Mutation von Zeilen erhalten, die = count (table1) * count (table2);

Ihr Problem besteht wirklich mit Teil 2 meiner Antwort. (mit Ihrer zweiten Anfrage)


In beiden Tabellen befinden sich mehr Zeilen als ich zeige. In beiden Tabellen gibt es die ID des Benutzers, als den Sie angemeldet sind. Würde die Teilnahme an dieser Hilfe helfen?
Rocket Hazmat

Nun, Sie müssen nur etwas ändern, wenn Ihre erste Abfrage nicht die erwarteten Ergebnisse zurückgibt. Wenn die erste Abfrage die gewünschten Ergebnisse zurückgibt, gibt es wirklich kein Problem. Ich dachte du wärst nur neugierig warum die 2 nicht das gleiche Ergebnis zeigen.
Superfro

@superfro, ich bin gespannt, warum die 2 nicht das gleiche Ergebnis zeigen.
Rocket Hazmat

Ihre zweite Abfrage verwendet ein where IN (Werte), wobei der Teil 'values' aus der Tabelle stammt, und es ist eine Zeichenfolge. Die Zeichenfolge wird als bool true ausgewertet, wobei = 1, weshalb nur die erste Zeile angezeigt wird.
Superfro

1
Wenn Sie sich Sorgen um die Leistung machen, sollten Sie wahrscheinlich über eine Änderung Ihrer Datenbankstruktur nachdenken. Sie können eine gemeinsame Tabelle hinzufügen, die zwei Werte enthält: order_ID und company_ID, anstatt die durch Kommas getrennte Liste in der Auftragstabelle zu verwenden. Auf diese Weise können Sie den Namen aus der Firma auswählen. Links beitreten order_companies auf company.company_ID = order_companies.company_ID links Join-Bestellungen auf order_companies.order_ID = order.order_ID wobei orders.order_ID = 1; Dies würde Indizes verwenden.
Superfro

-1

Lassen Sie mich erklären, wann FIND_IN_SET und wann IN verwendet werden soll.

Nehmen wir Tabelle A mit Spalten mit den Namen "aid", "aname". Nehmen wir Tabelle B mit den Spalten "bid", "bname", "aids".

Jetzt gibt es Dummy-Werte in Tabelle A und Tabelle B wie unten.

Tabelle A.

Hilfe aname

1 Apple

2 Banane

3 Mango

Tabelle B.

Gebot bname Hilfsmittel

1 Apfel 1,2

2 Banane 2,1

3 Mango 3,1,2

enter code here

Fall 1: Wenn Sie die Datensätze aus Tabelle b abrufen möchten, in denen 1 Wert in den Hilfsspalten vorhanden ist, müssen Sie FIND_IN_SET verwenden.

Abfrage: Wählen Sie * aus A JOIN B ON FIND_IN_SET (A.aid, b.aids), wobei A.aid = 1;

Fall 2: Wenn Sie diese Datensätze aus Tabelle a abrufen möchten, deren Wert 1 ODER 2 ODER 3 in Hilfsspalten enthält, müssen Sie IN verwenden.

Abfrage: Wählen Sie * aus A JOIN B ON A.aid IN (b.aids);

Nun hier bis zu Ihnen, was Sie durch MySQL-Abfrage benötigen.


Diese Frage wurde bereits gelöst. Ich glaube auch nicht, dass Ihr zweites Beispiel mit IN funktioniert ... das war im Grunde das Problem, das ich am Anfang zu lösen versuchte.
Rocket Hazmat

-2
SELECT o.*, GROUP_CONCAT(c.name) FROM Orders AS o , Company.c
    WHERE FIND_IN_SET(c.CompanyID , o.attachedCompanyIDs) GROUP BY o.attachedCompanyIDs

6
Willkommen bei SO! Code ohne Erklärungen ist selten hilfreich. In diesem Fall wird nicht einmal versucht, die Frage "Warum ...?" Zu beantworten. Bitte beachten Sie auch, dass diese spezielle Frage bereits eine akzeptierte Antwort hat, die eine gut erhaltene Antwort (> 80 Stimmen!) Gibt. Als neuer Benutzer ist es möglicherweise am besten, sich auf unbeantwortete Fragen zu konzentrieren und / oder selbst gute Fragen zu stellen.
cfi
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.