Mysql
 sql >> Datenbank >  >> RDS >> Mysql

So geben Sie eine Ergebnismenge basierend auf anderen Zeilen zurück

Hier sind zwei verschiedene Lösungen:(Hinweis:Ich habe das Enum-Feld "package_type" genannt)

1. Lösung (über IF()-Funktion):

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)

Diese Lösung nimmt im Wesentlichen die Speicherorte und verbindet sie mit dem Paket mit general (von dem angenommen wird, dass es existiert; daher inner join ) und spezielles Paket (das optional ist; daher left join ). Es erstellt Datensätze wie diesen:

location | general-package | [special-package]

Es verwendet dann das MySQL IF Funktion, um zuerst zu versuchen, die ID eines speziellen Pakets auszuwählen, und greift dann auf die ID des allgemeinen Pakets zurück.

2. Lösung (über Umwandlung von Enum in Integer):

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)

Diese Lösung nutzt die Tatsache aus, dass Aufzählungen als ganze Zahlen gespeichert werden. Es wandelt die Aufzählung in eine Ganzzahl um. special in diesem Fall wird 2 zurückgegeben und general gibt 1 zurück . Da diese Sonderzeichen in diesem Fall garantiert höher sind als die allgemeinen (d. h. 2> 1), können wir das MAX verwenden aggregierte Funktion. Jetzt haben wir im Wesentlichen eine Tabelle der Standorte und ihres "empfohlenen Pakets" (dh speziell, falls vorhanden, allgemein sonst). Wir verbinden dies einfach mit der normalen Abfrage zusammen mit dem erwarteten Pakettyp und es gibt die richtigen Ergebnisse zurück.

Haftungsausschluss:Ich bin mir über die Effizienz dieser beiden Methoden nicht sicher, daher möchten Sie dies vielleicht selbst testen.

Wenn Sie die Tabelle entweder neu gestalten oder aus Effizienzgründen denormalisieren möchten, ist dieses Design meiner Meinung nach besser geeignet:

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

Der Vorteil wäre, dass es einfacher ist, mehrere Regeln auf Datenbankebene durchzusetzen:

  • Ein Standort muss immer ein allgemeines Paket haben (Items.general_package_id könnte als NOT NULL definiert werden)
  • Ein Standort darf nur ein einziges allgemeines Paket haben (das Hinzufügen in einem Feld statt in einem Join garantiert, dass nur eines angegeben ist)
  • Ein Standort darf höchstens ein einziges spezielles Paket haben (das Hinzufügen in einem Feld statt in einem Join garantiert, dass nur eines angegeben ist)
  • Ein Fremdschlüssel auf Items.general_package_id =GeneralPackages.id würde garantieren, dass diese Spalte nur gültige Pakete enthält, die "allgemein" sind.
  • Dasselbe könnte man für special_package_id machen.

Der Nachteil wäre, dass Sie wahrscheinlich jedes Mal UNION ALL verwenden müssten, wenn Sie eine Ihrer alten Abfragen verwenden.