Voici deux solutions différentes :(Remarque :j'ai appelé le champ d'énumération "package_type")
1ère solution (via la fonction 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)
Cette solution prend essentiellement les emplacements et les joint au package avec general (qui est supposé exister ; d'où inner join
) et package spécial (qui est facultatif ; d'où left join
). Il crée des enregistrements comme celui-ci :
location | general-package | [special-package]
Il utilise ensuite le MySQL IF
fonction pour essayer d'abord de choisir l'ID du package spécial, puis revient à l'ID du package général.
2ème solution (via la conversion d'énumération en entier) :
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)
Cette solution exploite le fait que les énumérations sont stockées sous forme d'entiers. Il convertit l'énumération en un entier. special
dans ce cas renverra 2
et general
renverra 1
. Parce que ces spéciaux sont garantis supérieurs aux généraux dans ce cas (c'est-à-dire 2> 1), nous pouvons utiliser le MAX
fonction d'agrégation. Maintenant, nous avons essentiellement un tableau des emplacements et leur "forfait recommandé" (c'est-à-dire spécial s'il existe, général sinon). Nous joignons simplement ceci à la requête normale avec le type de package attendu, et il renvoie les résultats corrects.
Avis de non-responsabilité :je ne suis pas sûr de l'efficacité de l'une ou l'autre de ces méthodes, vous pouvez donc tester cela par vous-même.
Si vous cherchez à reconcevoir la table ou à la dénormaliser pour plus d'efficacité, je pense que cette conception peut être plus appropriée :
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
L'avantage serait qu'il serait plus simple d'appliquer plusieurs règles au niveau de la base de données :
- Un emplacement doit toujours avoir un package général (Items.general_package_id peut être défini comme NOT NULL)
- Un emplacement ne doit avoir qu'un seul package général (l'ajouter dans un champ plutôt qu'une jointure garantit qu'il n'y en a qu'un seul spécifié)
- Un emplacement peut avoir au plus un seul package spécial (l'ajouter dans un champ plutôt qu'une jointure garantit qu'il n'y en a qu'un seul spécifié)
- Une clé étrangère sur Items.general_package_id =GeneralPackages.id garantirait que cette colonne ne contient que des packages valides qui sont "généraux".
- La même chose pourrait être faite pour special_package_id.
L'inconvénient serait que vous auriez probablement besoin d'utiliser UNION ALL à chaque fois que vous utilisez l'une de vos anciennes requêtes.