Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment renvoyer un ensemble de résultats basé sur d'autres lignes

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.