FIND_IN_SET () vs IN ()


125

Ho 2 tabelle nel mio database. Uno è per gli ordini e uno è per le aziende.

Gli ordini hanno questa struttura:

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

E la società ha questa struttura:

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

Per ottenere i nomi delle società di un ordine, posso fare una domanda come tale:

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

Quella query funziona bene, ma la seguente query no.

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

Perché la prima query funziona ma non la seconda?

La prima query restituisce:

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

La seconda query restituisce solo:

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

Perché, perché la prima query restituisce tutte le società, ma la seconda query restituisce solo la prima?


3
attachCompanyIDs è una grande stringa, quindi mysql prova a trovare compagnia in questo suo cast a numero intero
Haim Evgi

Penso che questo sia il miglior esempio mysqltutorial.org/mysql-find_in_set
Shurvir Mori,

Risposte:


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

attachedCompanyIDsè un valore scalare che viene inserito in INT(tipo di companyID).

Il cast restituisce solo numeri fino alla prima non cifra (una virgola nel tuo caso).

Così,

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

In PostgreSQL, potresti lanciare la stringa in array (o memorizzarla come array in primo luogo):

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

e questo userebbe persino un indice su companyID.

Sfortunatamente, questo non funziona MySQLpoiché quest'ultimo non supporta gli array.

Potresti trovare questo articolo interessante (vedi #2):

Aggiornare:

Se esiste un limite ragionevole al numero di valori negli elenchi separati da virgola (diciamo, non più di 5), quindi puoi provare a usare questa query:

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
Grazie per la spiegazione. Non mi rendevo conto che il campo allegatoCompanyIDs è stato trasmesso a un INT. C'è un modo per aggirare questo in MySQL? FIND_IN_SETfunziona, ma non utilizza gli indici e potrebbe essere lento con molte informazioni nella tabella Azienda.
Rocket Hazmat,

1
Puoi spiegarci quell'aggiornamento? Cosa fa esattamente, perché sembra funzionare.
Rocket Hazmat,

1
@Rocket: rimuove gli poselementi dall'inizio del CVSe getta il resto in numero intero.
Quassnoi,

9
Pollice in su (y) per10 things in MySQL (that won’t work as expected)
NullPointer

@Quassnoi, perché scrivi CROSS JOIN? Non sono tutti uguali in MySQL?
Pacerier,

13

attachCompanyIDs è una stringa grande, quindi mysql cerca di trovare compagnia in questo suo cast intero

quando usi dove

quindi se comapnyid = 1:

companyID IN ('1,2,3')

questo è ritorno vero

ma se il numero 1 non è in primo luogo

 companyID IN ('2,3,1')

il suo ritorno è falso


3

Per ottenere il nome di tutte le società collegate, non basato su un ID specifico.

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

1

poiché la seconda query cerca righe con l'ID 1 OR 2 OR 3, la prima query cerca uno dei valori delimitati da virgole che esiste in companyID,

e un altro problema qui è che non stai unendo le tabelle su una chiave comune nel tuo dove quindi otterrai una mutazione di righe che = count (table1) * count (table2);

Il tuo problema esiste davvero con la parte 2 della mia risposta. (con la tua seconda query)


Ci sono più righe in entrambe le tabelle di quelle che sto mostrando. In entrambe le tabelle, è presente l'ID dell'utente con cui si è effettuato l'accesso, per partecipare a tale aiuto?
Rocket Hazmat,

Bene, devi solo cambiare qualcosa se la tua prima query non restituisce i risultati previsti. Se la prima query restituisce i risultati desiderati, non c'è davvero alcun problema. Pensavo fossi solo curioso del perché i 2 non mostrano lo stesso risultato.
Superfro

@superfro, sono curioso di sapere perché i 2 non mostrano lo stesso risultato.
Rocket Hazmat,

la tua seconda query sta usando un IN (valori) dove la parte 'valori' proviene dalla tabella, ed è una stringa. La stringa viene valutata come un valore bool true che = 1, motivo per cui mostra solo la prima riga.
Superfro

1
Se sei preoccupato per le prestazioni, dovresti probabilmente pensare a cambiare la struttura del tuo database. È possibile aggiungere una tabella comune che contiene 2 valori, ID_ordine e ID_azienda invece di utilizzare l'elenco delimitato da virgole nella tabella degli ordini. Ciò consentirebbe di selezionare il nome dalla società a sinistra join order_companies su company.company_ID = order_companies.company_ID ha lasciato gli ordini join su order_companies.order_ID = order.order_ID dove ordini.order_ID = 1; Questo userebbe gli indici.
Superfro

-1

Mi spiego quando usare FIND_IN_SET e quando usare IN.

Prendiamo la tabella A che ha colonne denominate "help", "aname". Prendiamo la tabella B che ha colonne denominate "bid", "bname", "aids".

Ora ci sono valori fittizi nella Tabella A e Tabella B come di seguito.

Tabella A

aiutare aname

1 mela

2 banane

3 Mango

Tabella B

aiuti per il nome dell'offerta

1 mela 1,2

2 banane 2,1

3 Mango 3,1,2

enter code here

Caso 1: se si desidera ottenere quei record dalla tabella b che ha 1 valore presente nelle colonne degli aiuti, è necessario utilizzare FIND_IN_SET.

Query: selezionare * da A JOIN B ON FIND_IN_SET (A.aid, b.aids) dove A.aid = 1;

Caso2: se si desidera ottenere quei record dalla tabella a che ha 1 o 2 o 3 valore presente nelle colonne di aiuto, è necessario utilizzare IN.

Query: selezionare * da A JOIN B ON A.aid IN (b.aids);

Ora ecco a te quello che ti serve attraverso la query mysql.


Questa domanda era già stata risolta. Inoltre non credo che il tuo secondo esempio, con IN, funzioni ... quello era fondamentalmente il problema che stavo cercando di risolvere all'inizio.
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
Benvenuti in SO! Il codice senza spiegazioni è raramente utile. In questo caso non prova nemmeno a rispondere alla domanda "Perché ...?". Si noti inoltre che questa particolare domanda ha già una risposta accettata che dà una risposta ben accolta (> 80 voti!). Come nuovo utente, potrebbe essere meglio concentrarsi su domande senza risposta e / o porre personalmente delle domande valide.
cfi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.