2009-12-17 6 views
6

w mysql, chcę zaznaczyć dolne 2 pozycje z każdej kategoriiSQL Query wybrać dno 2 z każdej kategorii

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 

Nadanie mnie:

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

Zanim migracji z sqlite3 miałem aby najpierw wybrać najniższą z każdej kategorii, a następnie wykluczając wszystko, co do niej dołączyło, musiałem ponownie wybrać najniższą z każdej kategorii. Wtedy wszystko co najmniej równe temu nowemu najniższemu lub mniej w danej kategorii wygrywa. To by również wybrało więcej niż 2 w przypadku remisu, co było denerwujące ... Miało również bardzo długi czas działania.

Moim ostatecznym celem jest policzenie, ile razy dana osoba znajduje się w jednej z dwóch najniższych kategorii (istnieje również pole nazwy) i jest to jedna część, której nie umiem. Dzięki

+0

Czy istnieje również identyfikator, który jest unikalny dla każdego wiersza? –

+1

Skoro nie lubisz więzi, jak zamierzasz ich unikać? Każde rozwiązanie, które ktoś wymyśli, będzie musiało w jakiś sposób poradzić sobie z więzami, więc powinieneś starać się dokładnie określić zasady, które nimi rządzą. –

+2

Oznacziłem to pytanie jako "największy-na-grupę", ponieważ jest podobny do wielu innych pytań zadawanych przez StackOverflow za pomocą tego znacznika. Chociaż rozumiem, że pytasz o * najmniejsze wartości w grupie, technika rozwiązania tego problemu jest taka sama. –

Odpowiedz

4

można spróbować to:

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 

To powinno dać pożądanych rezultatów, ale sprawdzić, czy wydajność jest OK.

+0

To nie działa. Wraca do wersji 9.2 i 10.3 dla kategorii 2. –

+0

Przykro nam to słyszeć. Próbowałem i działa to dla mnie. Czy możesz sprawdzić, czy twoje dane testowe są poprawne? Dzięki! –

+0

Tak, wprowadziłem go dokładnie tak, jak widnieje powyżej, w tej samej kolejności i wszystkich. Wartości dla kategorii 1 powracają poprawnie (1.3 i 1.6), ale dla kategorii 2 jest ona zła, a dla 2 także (zwraca 4 i 16). Ponadto zapytanie to nawet nie zostanie wykonane, dopóki nie podasz aliasu do pierwszego podwybicia. –

1

Związek powinien działać. Nie jestem pewien wydajności w porównaniu do rozwiązania Petera.

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

Istnieje literówka w klauzuli where sub-select, powinna być "WHERE smallest.category = second_smallest.category". –

+1

Ponadto, nie da to poprawnych wyników, jeśli istnieje remis dla najmniejszej wartości w danej kategorii. –

+0

Aby pozbyć się więzi, po prostu dodaj 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; 

Testowane na MySQL 5.1.41 z danymi testowymi. Wyjście:

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

(. Dodatkowe miejsca dziesiętne są bo zadeklarował kolumnę value jako NUMERIC(9,2))

Podobnie jak inne rozwiązania, to produkuje więcej niż 2 wiersze na kategorie czy istnieją więzi. Istnieją sposoby na stworzenie warunku dołączenia, aby to rozwiązać, ale musimy użyć klucza podstawowego lub unikalnego klucza w tabeli, a także będziemy musieli wiedzieć, jak rozwiązać więzy.

+0

to jest świetne! dokładnie to, czego szukałem! dzięki! –

1

Oto bardzo ogólne rozwiązanie, które zadziałałoby przy wyborze pierwszych n wierszy dla każdej kategorii. To zadziała, nawet jeśli istnieją duplikaty wartości.

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

Oto wynik.

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

To jest testowany na MySQL 5.0.88 Zauważ, że początkowa wartość zmiennej @trk nie powinno być co najmniej wartość polu Kategoria.

1

Oto rozwiązanie, które prawidłowo obsługuje duplikaty. Nazwa tabeli jest „zzz” i kolumny są int i pływak

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 

Ostrzeżenia:

  • Jeśli jest tylko jedna wartość dla danej kategorii, a jedynie, że jeden wpis jest zwracana.
  • Jeśli istniał unikatowy identyfikator rekordu dla każdego wiersza, nie byłyby potrzebne wszystkie konkatasy do symulacji unikalnego klucza.

Twój przebieg mogą się różnić,

--Mark