2009-12-17 12 views
6

in MySQL, voglio selezionare i 2 elementi di fondo di ogni categoriaquery SQL per selezionare in basso a 2 per ogni categoria

Category Value 
1  1.3 
1  4.8 
1  3.7 
1  1.6 
2  9.5 
2  9.9 
2  9.2 
2  10.3 
3  4 
3  8 
3  16 

mi Dare:

Category Value 
1  1.3 
1  1.6 
2  9.5 
2  9.2 
3  4 
3  8 

Prima ho migrato da sqlite3 ho avuto per prima cosa selezionare un valore più basso da ogni categoria, quindi escludendo tutto ciò che si è unito a quello, ho dovuto selezionare nuovamente il più basso da ogni categoria. Quindi qualcosa di uguale a quello nuovo più basso o meno in una categoria vinta. Questo avrebbe anche scelto più di 2 in caso di parità, il che era fastidioso ... Aveva anche un runtime molto lungo.

Il mio obiettivo finale è contare il numero di volte in cui un individuo è in uno dei 2 più bassi di una categoria (c'è anche un campo nome) e questa è l'unica parte che non so come fare. Grazie

+0

C'è anche un ID che è unico per ogni riga? –

+1

Dato che non ti piacciono i legami, come pensi di evitarli? Qualsiasi soluzione che verrà a crearsi in qualche modo dovrà fare i conti con i legami, quindi dovresti cercare di specificare esplicitamente le regole che li governano. –

+2

Ho taggato questa domanda con 'greatest-n-per-group' perché è simile a molte altre domande poste su StackOverflow con quel tag. Anche se capisco che stai chiedendo i * minimi * valori per gruppo, la tecnica per risolverlo è la stessa. –

risposta

4

si potrebbe provare questo:

SELECT * FROM (
    SELECT c.*, 
     (SELECT COUNT(*) 
     FROM user_category c2 
     WHERE c2.category = c.category 
     AND c2.value < c.value) cnt 
    FROM user_category c) uc 
WHERE cnt < 2 

dovrebbe dare i risultati desiderati, ma controllare se le prestazioni sono ok.

+0

Questo non funziona. Restituisce il 9.2 e il 10.3 per la categoria 2. –

+0

Mi spiace sentirlo. L'ho provato e funziona per me. Potresti verificare se i dati del test sono corretti? Grazie! –

+0

Sì, l'ho inserito esattamente come appare sopra, nello stesso ordine e tutto. I valori per la categoria 1 tornano corretti, (1.3 e 1.6) ma per la categoria 2 è sbagliato, e anche per 2 (restituisce 4 e 16). Inoltre, questa query non viene eseguita fino a quando non si assegna un alias alla prima selezione secondaria. –

1

Un sindacato dovrebbe funzionare. Non sono sicuro delle prestazioni rispetto alla soluzione di Peter.

SELECT smallest.category, MIN(smallest.value) 
    FROM categories smallest 
GROUP BY smallest.category 
UNION 
SELECT second_smallest.category, MIN(second_smallest.value) 
    FROM categories second_smallest 
    WHERE second_smallest.value > (SELECT MIN(smallest.value) FROM categories smallest WHERE second.category = second_smallest.category) 
GROUP BY second_smallest.category 
+0

C'è un refuso nella clausola where della sottoselezione, dovrebbe essere "WHERE smallest.category = second_smallest.category". –

+1

Inoltre, questo non darà i risultati corretti se c'è un pareggio per il valore più piccolo in una data categoria. –

+0

Per eliminare le cravatte, basta aggiungere DISTINCT? –

8
SELECT c1.category, c1.value 
FROM catvals c1 
LEFT OUTER JOIN catvals c2 
    ON (c1.category = c2.category AND c1.value > c2.value) 
GROUP BY c1.category, c1.value 
HAVING COUNT(*) < 2; 

testato con MySQL 5.1.41 con i tuoi dati di test. Uscita:

+----------+-------+ 
| category | value | 
+----------+-------+ 
|  1 | 1.30 | 
|  1 | 1.60 | 
|  2 | 9.20 | 
|  2 | 9.50 | 
|  3 | 4.00 | 
|  3 | 8.00 | 
+----------+-------+ 

(. Le cifre decimali extra sono perché ho dichiarato la colonna value come NUMERIC(9,2))

Come altre soluzioni, questo produce più di 2 righe per categoria se ci sono legami. Ci sono modi per costruire la condizione di join per risolverlo, ma dovremmo usare una chiave primaria o una chiave univoca nella tua tabella, e dovremmo anche sapere come vuoi che i legami siano risolti.

+0

questo è fantastico! esattamente quello che stavo cercando! Grazie! –

1

Ecco una soluzione molto generalizzata, che funzionerebbe per la selezione delle prime n righe per ogni categoria. Funzionerà anche se ci sono duplicati in valore.

/* creating temporary variables */ 
mysql> set @cnt = 0; 
mysql> set @trk = 0; 

/* query */ 
mysql> select Category, Value 
     from (select *, 
       @cnt:=if(@trk = Category, @cnt+1, 0) cnt, 
       @trk:=Category 
       from user_categories 
       order by Category, Value) c1 
     where c1.cnt < 2; 

Ecco il risultato.

+----------+-------+ 
| Category | Value | 
+----------+-------+ 
|  1 | 1.3 | 
|  1 | 1.6 | 
|  2 | 9.2 | 
|  2 | 9.5 | 
|  3 |  4 | 
|  3 |  8 | 
+----------+-------+ 

questo è testato su MySQL 5.0.88 Si noti che il valore iniziale di variabile @trk non dovrebbe essere il valore minimo del campo Categoria.

1

Ecco una soluzione che gestisce correttamente i duplicati. nome della tabella è 'zzz' e le colonne sono int e float

select 
    smallest.category category, min(smallest.value) value 
from 
    zzz smallest 
group by smallest.category 

union 

select 
    second_smallest.category category, min(second_smallest.value) value 
from 
    zzz second_smallest 
where 
    concat(second_smallest.category,'x',second_smallest.value) 
    not in (-- recreate the results from the first half of the union 
     select concat(c.category,'x',min(c.value)) 
     from zzz c 
     group by c.category 
    ) 
group by second_smallest.category 

order by category 

Avvertenze:

  • Se v'è un solo valore per una determinata categoria, allora solo quella singola voce viene restituito.
  • Se esistesse un recordID univoco per ogni riga, non occorrerebbero tutti i concat per simulare una chiave univoca.

tua situazione potrebbe essere diversa,

--mark

Problemi correlati