Hier zijn twee verschillende oplossingen:(Opmerking:ik noemde het enum-veld "package_type")
Eerste oplossing (via IF()-functie):
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)
Deze oplossing neemt in wezen de locaties en voegt deze samen aan het pakket met algemeen (waarvan wordt aangenomen dat het bestaat; vandaar inner join
) en speciaal pakket (dat optioneel is; vandaar left join
). Het creëert records zoals deze:
location | general-package | [special-package]
Het gebruikt dan de MySQL IF
functie om eerst te proberen de ID van het speciale pakket te kiezen en vervolgens terug te vallen op de ID van het algemene pakket.
2e oplossing (via casten van enum naar geheel getal):
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)
Deze oplossing maakt gebruik van het feit dat opsommingen worden opgeslagen als gehele getallen. Het werpt de enum naar een geheel getal. special
in dit geval retourneert 2
en general
retourneert 1
. Omdat deze special in dit geval gegarandeerd hoger is dan algemeen (d.w.z. 2> 1), kunnen we de MAX
gebruiken geaggregeerde functie. Nu hebben we in wezen een tabel met de locaties en hun "aanbevolen pakket" (d.w.z. speciaal als het bestaat, anders algemeen). We voegen dit eenvoudig toe aan de normale zoekopdracht, samen met het verwachte pakkettype, en het geeft de juiste resultaten.
Disclaimer:ik ben niet zeker van de efficiëntie van een van deze methoden, dus misschien wilt u dit zelf testen.
Als u de tafel opnieuw wilt ontwerpen of deze wilt denormaliseren voor efficiëntie, denk ik dat dit ontwerp geschikter is:
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
Het voordeel zou zijn dat het eenvoudiger is om verschillende regels op databaseniveau af te dwingen:
- Een locatie moet altijd een algemeen pakket hebben (Items.general_package_id kan worden gedefinieerd als NOT NULL)
- Een locatie mag slechts één algemeen pakket hebben (toevoegen in een veld in plaats van een samenvoeging garandeert dat er maar één gespecificeerd is)
- Een locatie mag maximaal één speciaal pakket hebben (toevoegen in een veld in plaats van een join garandeert dat er maar één gespecificeerd is)
- Een externe sleutel op Items.general_package_id =GeneralPackages.id zou garanderen dat die kolom alleen geldige pakketten bevat die "algemeen" zijn.
- Hetzelfde kan worden gedaan voor special_package_id.
Het nadeel zou zijn dat u waarschijnlijk elke keer dat u een van uw oude zoekopdrachten gebruikt, een UNION ALL moet gebruiken.