Oto dwa różne rozwiązania:(Uwaga:nazwałem pole wyliczenia "package_type")
Pierwsze rozwiązanie (za pomocą funkcji IF()):
select
i.location,
if(ps.id is not null, ps.id, pg.id) as package_id
from
(select distinct location from Items) i
inner join
(select i.location, p.id
from Items i
inner join Packages p on (i.package_id = p.id and p.package_type = 'general')
) pg on (i.location = pg.location)
left join
(select i.location, p.id
from Items i
inner join Packages p on (i.package_id = p.id and p.package_type = 'special')
) ps on (i.location = ps.location)
To rozwiązanie zasadniczo pobiera lokalizacje i łączy je z pakietem z parametrem ogólnym (który zakłada się, że istnieje; stąd inner join
) i specjalny pakiet (który jest opcjonalny; stąd left join
). Tworzy rekordy takie jak:
location | general-package | [special-package]
Następnie używa MySQL IF
funkcja, aby najpierw spróbować wybrać identyfikator pakietu specjalnego, a następnie wrócić do identyfikatora pakietu ogólnego.
Drugie rozwiązanie (poprzez rzutowanie liczby enum na liczbę całkowitą):
select i.location, p.id
from
(select i.location, max(cast(package_type as unsigned)) as package_type
from Items i
left join Packages p on (i.package_id = p.id)
group by location
) i
inner join
(select i.location, p.id, p.package_type
from Items i
inner join Packages p on (i.package_id = p.id)
) p on (i.location = p.location and i.package_type = p.package_type)
To rozwiązanie wykorzystuje fakt, że wyliczenia są przechowywane jako liczby całkowite. Rzuca wyliczenie na liczbę całkowitą. special
w tym przypadku zwróci 2
i general
zwróci 1
. Ponieważ gwarantujemy, że te specjalne są w tym przypadku wyższe niż ogólne (tj. 2> 1), możemy użyć MAX
funkcja agregująca. Teraz zasadniczo mamy tabelę lokalizacji i ich "zalecanego pakietu" (tj. Specjalnego, jeśli istnieje, ogólnego w przeciwnym razie). Po prostu łączymy to z normalnym zapytaniem wraz z oczekiwanym typem pakietu i zwracamy poprawne wyniki.
Zastrzeżenie:nie jestem pewien co do skuteczności którejkolwiek z tych metod, więc warto przetestować to samodzielnie.
Jeśli chcesz przeprojektować tabelę lub zdenormalizować ją pod kątem wydajności, myślę, że ten projekt może być bardziej odpowiedni:
GeneralPackages table
id, name
1, General Package 1
SpecialPackages table
id, name
1, Special Package 1
2, Special Package 2
Items table
id, general_package_id, special_package_id, location
1, 1, NULL, America
2, 1, 2, Europe
Zaletą byłoby łatwiejsze egzekwowanie kilku reguł na poziomie bazy danych:
- Lokalizacja musi zawsze mieć pakiet ogólny (Items.general_package_id może być zdefiniowany jako NOT NULL)
- Lokalizacja musi mieć tylko jeden ogólny pakiet (dodanie go w polu zamiast połączenia gwarantuje, że jest tylko jeden określony)
- Lokalizacja może mieć co najwyżej jeden specjalny pakiet (dodanie go w polu zamiast połączenia gwarantuje, że jest tylko jeden określony)
- Klucz obcy w Items.general_package_id =GeneralPackages.id gwarantuje, że ta kolumna zawiera tylko prawidłowe pakiety, które są „ogólne”.
- To samo można zrobić dla special_package_id.
Wadą byłoby to, że prawdopodobnie będziesz musiał używać UNION ALL za każdym razem, gdy używasz jednego ze swoich starych zapytań.