2009-04-24 27 views
3

Ich habe eine Reihe von drei Tabellen:MySQL: Mehrspaltiger Join über mehrere Tabellen hinweg?

 
Dining_Tables; 
+--------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+--------------------+--------------+------+-----+---------+-------+ 
| dining_table  | int(11)  | NO | PRI | NULL |  | 
| bus_boy   | varchar(35) | NO |  | NULL |  | 
| waiter    | varchar(35) | NO |  | NULL |  | 
| server    | varchar(35) | NO |  | NULL |  | 
+--------------------+--------------+------+-----+---------+-------+ 

Poker_Tables; 
+--------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+--------------------+--------------+------+-----+---------+-------+ 
| poker_table  | int(11)  | NO | PRI | NULL |  | 
| dealer    | varchar(35) | NO |  | NULL |  | 
| pit_boss   | varchar(35) | NO |  | NULL |  | 
+--------------------+--------------+------+-----+---------+-------+ 

Computer_Tables; 
+--------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+--------------------+--------------+------+-----+---------+-------+ 
| computer_table  | int(11)  | NO | PRI | NULL |  | 
| programmer   | varchar(35) | NO |  | NULL |  | 
+--------------------+--------------+------+-----+---------+-------+ 

Jede dieser Zeilen hat global eindeutige Tabellen-ID mit ihm verbunden: (dining_table, poker_table, computer_table) Die anderen Spalten speichern Sie die Vor-/Nachname der Person die Rolle erfüllen.

In meinem Modell kann eine einzelne Person mehrere Dinge tun. Zum Beispiel könnte Joe Smith simultan an einem computer_table als Programmierer sitzen, bei einem poker_table als Händler sitzen und auf einen dining_table als Kellner warten.

Meine Frage ist dies: Ich möchte eine Abfrage, mit der ich alle table_ids für eine bestimmte Person abrufen kann. Insbesondere würde die Abfrage dann eine Liste von table_ids zurückgeben, an der sich Joe Smith momentan befindet.

Ich könnte etwas in diese Richtung tun:

 
select dining_table from Dining_Tables where 
     bus_boy = "Joe Smith" or 
     waiter = "Joe Smith" or 
     server = "Joe Smith"; 

select poker_table from Poker_Tables where 
     dealer = "Joe Smith" or 
     pit_boss = "Joe Smith"; 

select computer_table from Computer_Tables where 
     programmer = "Joe Smith"; 

Allerdings, das ist drei separate Abfragen und ich würde es vorziehen, wirklich tun, um zu vermeiden, dass, wenn möglich. Kann ich das in einer Abfrage mit Joins machen?

+0

Perphaps eine bessere Art des Titels der Frage ist MySQL: Multi- "ROW" Join über mehrere Tabellen? .. das ist, war Sie gefragt haben an erster Stelle ... Sie nicht zwei oder mehr zusammenführen SELECT "Spalten" mit einem Kriterium. – Victor

Antwort

6

Wenn Sie es in einer einzelnen Abfrage zurückgeben möchten, könnten Sie eine UNION-Anweisung verwenden, die diese drei Abfragen zu einer zusammenfasst.

select dining_table as Table from Dining_Tables where 
     bus_boy = "Joe Smith" or 
     waiter = "Joe Smith" or 
     server = "Joe Smith" 
UNION 
select poker_table as Table from Poker_Tables where 
     dealer = "Joe Smith" or 
     pit_boss = "Joe Smith" 
UNION 
select computer_table as Table from Computer_Tables where 
     programmer = "Joe Smith" 

Obwohl es drei Aussagen ist, würde es als eine einzige Ergebnismenge zurückgegeben werden.

Wenn Sie Ihre "Jobs" in eine separate Tabelle getrennt haben, würde das Abfragen viel einfacher machen.

konnte ich etwas entlang der Linien von vorstellen:

Tabellen, Tabellenarten, Personen, Rollen, TableRoles

TableTypes 
ID TypeName 
----------------------- 
1  Dining 
2  Poker 
3  Computer 

Tables 
ID TableTypeID 
---------------------------- 
1  1 
2  1 
3  2 
4  3 
5  2 
6  3 

Persons 
ID Name 
--------------------------- 
1  Joe Smith 
2  Mary Belcher 
3  Norma Grey 


Roles 
ID RoleName 
----------------------------- 
1  Bus Boy 
2  Waiter 
3  Server 
4  Dealer 
5  Pit Boss 
6  Programmer 


TableRoles 
TableID RoleID PersonID 
----------------------------- 
1   1   1 
//etc 
//etc 

Dies würde alles machen Ihrer sehr einfach und unkompliziert abfragen und auch skalierbar macht (So können Sie jeder Tabelle mehr Rollen hinzufügen, ohne Ihre Datenbank zu belästigen.)

8

Ihr Datenmodell ist nicht optimal. Bedenken Sie:

 
Person  PersonRole  Role  Table 
------  ----------  ----  ----- 
Id*  PersonId*  Id*  Id* 
Name  RoleId*  Name  Name 
          TableId 

Davon abgesehen ...

select dining_table from Dining_Tables where 
     bus_boy = "Joe Smith" or 
     waiter = "Joe Smith" or 
     server = "Joe Smith" 
union 
select poker_table from Poker_Tables where 
     dealer = "Joe Smith" or 
     pit_boss = "Joe Smith" 
union 
select computer_table from Computer_Tables where 
     programmer = "Joe Smith" 
+0

Für SQL Server möchten Sie möglicherweise eine UNION ALL erstellen, da UNION Duplikate entfernt, was ein Leistungseinbruch sein kann ... es sei denn, eine Person kann mehrere Plätze an einem Tisch haben. In diesem Fall ist UNION geeignet. Ich bin mir nicht sicher, ob Mysql die gleiche Semantik hat. –

+0

Ich wählte Union bewusst genau deswegen. Aber das OP ist sich nicht sicher, ob das passieren kann oder nicht, ich habe es nur angedeutet. – Tomalak

3

Ich glaube, Sie mit Ihrer Benutzertabelle beginnen sollten und kommen dann zu den anderen Tabellen von dort:

SELECT U.user_id, d.dining_table, p.poker_table, c.computer_table 
FROM Users U 
LEFT JOIN Dining_Table D 
    ON D.bus_boy = U.user_id OR D.waiter = U.user_id or D.server = U.user_id 
LEFT JOIN Poker_Table P 
    ON P.dealer = U.user_id OR P.pit_boss = U.user_id 
LEFT JOIN Computer_Table C 
ON C.programmer = U.user_id 
WHERE U.Name = 'Joe Smith' 

-- only return rows with at least one table match 
-- or leave off to always return the user) 
AND NOT (
    d.dining_table IS NULL 
    AND p.poker_table IS NULL 
    AND c.computer_table IS NULL 
) 

Dieser Willen geben Sie eine Zeile pro Benutzer an und geben Sie an, in welcher Tabelle der Benutzer ist (oder null).

Wenn Sie eine direkte Liste nur für einen einzelnen Benutzer haben möchten, ist der Union-Ansatz möglicherweise vorzuziehen. Wenn diese Abfrage jedoch für einen Dienstplan oder Benutzer ausgeführt wird, denke ich, dass mein Ansatz besser ist.