2009-12-17 11 views
6

In Mysql wählen, möchte ich die unteren 2 Artikel aus jeder KategorieSQL Query Ende 2 aus jeder Kategorie

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 

Giving mich wählen:

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

Bevor ich von sqlite3 gewandert hatte ich um zuerst eine niedrigste von jeder Kategorie auszuwählen, und dann alles ausschließen, was sich damit verband, musste ich wieder das niedrigste aus jeder Kategorie auswählen. Dann hat alles, was dem neuen niedrigsten oder weniger in einer Kategorie entspricht, gewonnen. Dies würde auch mehr als 2 im Falle eines Unentschieden wählen, was nervig war ... Es hatte auch eine wirklich lange Laufzeit.

Mein ultimatives Ziel ist es zu zählen, wie oft sich eine Person in einer der niedrigsten 2 einer Kategorie befindet (es gibt auch ein Namensfeld) und dies ist der eine Teil, den ich nicht kenne. Dank

+0

Gibt es auch eine ID, die für jede Zeile einzigartig ist? –

+1

Da Sie die Krawatten nicht mögen, wie planen Sie, sie zu vermeiden? Jede Lösung, mit der irgendjemand konfrontiert wird, muss sich in irgendeiner Weise mit Beziehungen auseinandersetzen, also sollten Sie versuchen, die Regeln, die für sie gelten, genau zu formulieren. –

+2

Ich habe diese Frage mit 'greatest-n-per-group' getaggt, weil sie ähnlich wie viele andere Fragen in StackOverflow mit diesem Tag ist. Obwohl ich verstehe, dass Sie nach den * kleinsten * Werten pro Gruppe fragen, ist die Technik zur Lösung gleich. –

Antwort

4

Sie konnten dieses versuchen:

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 

Es sollte Ihnen die gewünschten Ergebnisse, aber überprüfen, ob die Leistung in Ordnung ist.

+0

Das funktioniert nicht. Es gibt 9.2 und 10.3 für Kategorie 2. –

+0

Sorry das zu hören. Ich habe es versucht und es funktioniert für mich. Könnten Sie bitte überprüfen, ob Ihre Testdaten korrekt sind? Vielen Dank! –

+0

Ja, ich habe es genau so eingegeben, wie es oben erscheint, in der gleichen Reihenfolge und alles. Die Werte für Kategorie 1 sind korrekt, (1.3 und 1.6), aber für Kategorie 2 ist es falsch, und für 2 ebenfalls (gibt 4 und 16 zurück). Diese Abfrage wird auch nicht ausgeführt, bis Sie der ersten Unterauswahl einen Alias ​​geben. –

1

Eine Union sollte funktionieren. Ich bin mir der Leistung im Vergleich zu Peters Lösung nicht sicher.

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

Es gibt einen Tippfehler in der where-Klausel der Unterauswahl, sollte "WHERE lastest.category = second_smallest.category" sein. –

+1

Auch wird dies nicht die richtigen Ergebnisse geben, wenn es eine Bindung für den kleinsten Wert in einer bestimmten Kategorie gibt. –

+0

Um Bindungen loszuwerden, fügen Sie einfach DISTINCT hinzu? –

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; 

auf MySQL getestet 5.1.41 mit Testdaten. Ausgang:

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

(. Die zusätzlichen Dezimalstellen sind, weil ich die value Spalte als NUMERIC(9,2) deklariert)

Wie andere Lösungen, diese produziert mehr als zwei Zeilen pro Kategorie, wenn Bindungen sind. Es gibt Möglichkeiten, die Join-Bedingung zu konstruieren, um das zu lösen, aber wir müssten einen Primärschlüssel oder einen eindeutigen Schlüssel in Ihrer Tabelle verwenden und wir müssten auch wissen, wie Sie beabsichtigen, Verbindungen zu lösen.

+0

das ist großartig! genau das was ich gesucht habe! Vielen Dank! –

1

Hier ist eine sehr generalisierte Lösung, die für die Auswahl der ersten n Zeilen für jede Kategorie funktionieren würde. Dies funktioniert auch bei doppelten Werten.

/* 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; 

Hier ist das Ergebnis.

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

Dies wird auf MySQL 5.0.88 Hinweis getestet, dass Anfangswert von @trk Variable der Kategorie Feld nicht den geringsten Wert sein sollte.

1

Hier ist eine Lösung, die Duplikate richtig behandelt. Tabellenname ist ‚zzz‘ und Spalten sind int und 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 

Caveats:

  • Wenn nur ein Wert für eine bestimmte Kategorie ist, dann nur, dass einzelne Eintrag zurückgegeben.
  • Wenn es eine eindeutige RecordID für jede Zeile gäbe, würden Sie nicht alle Concats benötigen, um einen eindeutigen Schlüssel zu simulieren.

Leistung kann variieren,

--Mark