2009-10-18 22 views
7

ho attualmente la seguente riga nel mio tavolo:MySQL istruzione Select, DOVE E 'IN' clausola

  course_data: 
      user_id  days  <-- This is a varchar column. 
       405   1,3,5 

e sto cercando di implementare la seguente istruzione SELECT:

SELECT usrID, usrFirst, usrLast, usrEmail 
    FROM tblUsers 
    WHERE usrID NOT IN 
    (
     SELECT users.usrID 
      FROM 
       `course_data` courses, 
       `tblUsers` users 
      WHERE 
       days IN ('$day') 
    ) 
    GROUP BY usrID 
    ORDER BY usrID 

Fondamentalmente, io desidera che la riga (con utente 405) venga omessa se la variabile $ day include un '1, 3 o 5'.

Ad esempio, se $day = "1", deve restituire una query vuota (poiché il numero "1" si trova nella colonna "1,3,5").

Tuttavia, non ho trovato questo il caso. Anche se $day = "1", restituisce ancora quella riga.

L'unico modo per non restituire la riga è se $day= "1,3,5." Eppure, ho pensato che la clausola IN() avrebbe preso qualsiasi parte della mia variabile e la avrebbe applicata a quella colonna.

Qualche idea su cosa sto facendo male qui? Grazie.

risposta

11

Si dovrebbe usare the like keyword per fare una corrispondenza parziale del campo char:

where days like '%1%' 

Edit: VolkerK e Lukáš Lalinský sia suggerire find_in_set di MySQL, che è probabilmente meglio di like per ciò che si vuole fare. Tuttavia, si applica ancora la seguente raccomandazione sulla normalizzazione del database.

Tuttavia, non è possibile memorizzare più valori in un singolo campo di database. Invece, considerare l'utilizzo di due tabelle:

course_data: 
    user_id 

course_days: 
    user_id 
    day_number 

Poi, si avrebbe i seguenti dati:

course_data: 
    user_id 
    405 

course_days 
    user_id day_number 
    405  1 
    405  3 
    405  5 

È quindi possibile eseguire una query in modo corretto questo schema. Per esempio:

select cd.user_id 
from course_data as cd 
where cd.user_id not in 
    (
     select course_days.user_id 
     from course_days 
     where course_days.user_id = cd.user_id 
      and course_days.day_number = 1 
    ) 

(o, che dovrebbe essere vicino, io non sono esattamente sicuro di quello che stai cercando di realizzare o di ciò che il resto dello schema assomiglia, quindi questa è un'ipotesi migliore) .

+0

Grazie. Hai ragione a cambiare la struttura. Sono andato avanti e l'ho fatto. Grazie. – Dodinas

+0

"quindi questa è la migliore ipotesi": un'altra idea (sperabilmente fattibile) è quella di usare una subquery NON ESISTA come descritto su http://dev.mysql.com/doc/refman/5.1/en/exists-and-not- exists-subqueries.html invece di 'x NOT IN (sottoquery)' – VolkerK

+0

% like% darebbe 11 per una ricerca di 1, quindi non è adatto, quindi questa risposta non si applica alla domanda originale. La risposta di theomega sembra più applicabile all'OP. – Tarquin

14

Rimuovere le virgolette nell'istruzione IN. La sintassi è:

... WHERE column IN (1,2,3) 

e non come è stato utilizzato

... WHERE column IN ('1,2,3') 

Vedere anche la documentation su IN, ci sono altri esempi.

+0

In realtà, mi dispiace, ho bisogno di qualche chiarimento. Sto facendo qualcosa di sbagliato qui. Se utilizzo: WHERE days IN (1), lo rimuove correttamente e restituisce un risultato vuoto. Ma se utilizzo: WHERE days IN (3), non restituisce una query vuota. Penserei che dovrebbe rimuovere la query perché '3' è nella query? Qualche idea? – Dodinas

+0

Direi che questo è perché converte "1,3,5" in un int (fermandosi alla virgola), e poi vede che corrisponde. – Yuliy

+0

Non sono sicuro del motivo per cui questa non è la risposta accettata per lo scenario indicato nel post originale, anche se accetto che la risposta accettata fornisca consigli sulla correzione delle cattive abitudini di codifica. – Tarquin

0

giorni non contiene un elenco di stringhe. Tiene una singola stringa. La stringa "1,3,5" è contenuta in una delle stringhe in {'1'}? Chiaramente la risposta è no. Entrambi giorni refactoring in una tabella diversa, o essere pronti a fare la manipolazione di più stringhe

2

Se ho compreso correttamente la tua domanda, vuoi che il contenuto del campo varchar sia trattato come un elenco separato da virgole e verificare se questo elenco non contiene un determinato valore. Per questo è necessaria la funzione find_in_set(str, strlist).
Ma tieni presente che MySQL non può utilizzare un indice in quel caso e la tua query avrà sempre bisogno di una scansione completa della tabella. È potrebbe essere meglio non memorizzare i dati strutturati (ed eseguire confronti sui singoli elementi) in una singola colonna ma utilizzare un'altra tabella e un JOIN come è stato suggerito in altre risposte.

5

credo che la query che si desidera è:

SELECT usrID, usrFirst, usrLast, usrEmail 
FROM tblUsers 
WHERE usrID NOT IN (
    SELECT user_id 
    FROM course_data 
    WHERE find_in_set(?, days) > 0 
) 
ORDER BY usrID 

ma si dovrebbe prendere seriamente in considerazione la normalizzazione del database, in modo che ogni giorno ha la sua propria riga.