2009-06-10 5 views
5

J'ai trois tables:se joindre à une table plusieurs fois à d'autres tables

Tableau utilisateur (nom d'utilisateur userid)

Tableau clé (userid keyid)

Table pour ordinateur portable (userid laptopid)

Je veux tous les utilisateurs qui ont une clé ou un ordinateur portable, ou les deux. Comment puis-je écrire la requête de sorte qu'il utilise une jointure entre table User et table Key, ainsi qu'une jointure entre table User et table Laptop?

Le principal problème est que dans le scénario actuel, il y a douze ou si jointures de table, STH comme:

", sélectionnez .. Dans une jointure gauche b sur (...), c rejoindre sur (..), e, f, g où ... ",

et je vois que a pourrait être joint à b, et un pourrait également être joint à f. Donc, en supposant que je ne puisse pas faire apparaître les tableaux a, b et f côte à côte, comment écrire la requête sql?

+0

Chaque utilisateur doit avoir une clé ou un ordinateur portable. Droite? –

+0

vous pourriez avoir raison, peut-être tort, mais si totalement ne vous souciez pas! encore amusant quand même :) – umar

+0

Q1: voulez-vous connaître seulement l'utilisateur (userid) ou aussi qu'est-ce qu'ils ont exactement? Q2: Un utilisateur peut-il avoir plus d'un article d'un type (2 ordinateurs portables ou 3 clés)? – van

Répondre

1
select distinct u.userid, u.username 
from User u 
    left outer join Key  /* k on u.userid = k.userid */ 
    left outer join Laptop /* l on u.userid = l.userid */ 
where k.userid is not null or l.userid is not null 

EDIT "Le principal problème est que dans le scénario actuel, il y a douze ou si jointures de table, STH comme: ", sélectionnez .. Dans une jointure gauche b sur (...) , c joindre d sur (..), e, f, g où ... ", et je vois que a pourrait être joint à b, et un pourrait également être joint à f. Donc, en supposant que je ne peux pas faire le les tables a, b et f apparaissent côte à côte, comment écrire la requête sql? "

Vous pouvez avoir autant de jointures externes gauche que nécessaire. Joignez la table avec la clé primaire au reste des tables ou sur tout autre champ où les valeurs de champ d'une table doivent correspondre aux valeurs de champ de l'autre table.

par exemple expliquera mieux que les mots

select * 
from a 
left outer join b on a.pk = b.fk -- a pk should match b fk 
left outer join c on a.pk = c.fk -- a pk should match c fk 
left outer join d on c.pk = d.fk -- c pk should match d fk 

et ainsi de suite

+0

J'ai complété la question pour montrer ma principale difficulté est de ne pas pouvoir faire apparaître les trois tables côte à côte, avec quelque chose comme des tables jointes complètes (je veux dire: "de c, d" etc) apparaissant mélangées entre – umar

+0

Edited et as Q –

+1

Vous avez toujours manqué une clause where comme "où k.userid n'est pas nul ou l.userid n'est pas nul" dans votre première réponse pour filtrer les utilisateurs sans rien. –

9

Vous pouvez utiliser plusieurs jointures pour combiner plusieurs tables:

select * 
from user u 
left join key k on u.userid = k.userid 
left join laptop l on l.userid = u.userid 

A "se joindre à gauche" trouve aussi les utilisateurs qui Je n'ai pas de clé ni d'ordinateur portable. Si vous remplacez les deux par "jointure interne", il ne trouvera que les utilisateurs avec un ordinateur portable et une clé.

Lorsqu'une «jointure à gauche» ne trouve pas de ligne, elle renvoie NULL dans ses champs. Ainsi, vous pouvez sélectionner tous les utilisateurs qui ont soit un ordinateur portable ou une clé comme celui-ci:

select * 
from user u 
left join key k on u.userid = k.userid 
left join laptop l on l.userid = u.userid 
where k.userid is not null or l.userid is not null 

NULL est spécial, que vous comparez comme « champ est non nulle » au lieu de « champ <> null ».

Ajouté après votre commentaire: Dites que vous avez une Table de la souris, c'est-à-dire liée à Ordinateur portable, mais pas à Utilisateur. Vous pouvez vous joindre à cela comme:

select * 
from user u 
left join laptop l on l.userid = u.userid 
left join mouse m on m.laptopid = l.laptopid 

Si cela ne répond pas à votre question, vous devez clarifier un peu plus.

+0

Je pense que le dernier u.userid devrait être k.userid –

+0

ouais! C'est bonne réponse comme ma réponse. +1 –

+0

désolé de ne pas avoir clarifié la question avant: j'ai ajouté plus de détails à la question pour que votre réponse ne semble pas résoudre mon problème – umar

1

Comme vous l'avez décrit, vous vouliez seulement savoir si quelqu'un avait un ordinateur portable ou une clé.Je voudrais écrire la requête avec une sous-requête plutôt que d'une jointure:

select * 
from user 
where userid in (select userid from key union select userid from laptop) 

La raison est que par une jointure une personne avec plusieurs ordinateurs portables ou plusieurs clés seront répertoriés plusieurs fois (sauf si vous utilisez distinct). Et même si vous utilisez distinct vous vous retrouvez avec une requête moins efficace (au moins sur Oracle l'optimiseur de requête ne semble pas être en mesure de créer un plan efficace).

+1

Il ne sera pas une jointure croisée si vous spécifiez la jointure externe gauche. La jointure externe gauche est plus rapide que la sous-requête. –

+0

Je l'ai essayé rapidement dans une base de données Oracle: vous avez raison, il ne provoque pas de jointure croisée. Mais ce n'était pas plus rapide que la méthode des sous-requêtes - en réalité plus de deux fois plus lente. – waxwing

+0

Je veux faire très peu de modifications à la requête sauf ajouter plus de clauses JOIN et ON, donc pour l'instant je cherche un moyen de le faire sans utiliser de sous-requêtes. J'ai aussi une base de données mysql dont le comportement peut être différent du comportement des oracles sur les requêtes imbriquées – umar

0
SELECT * 
FROM User 
LEFT JOIN Key ON User.id = Key.user_id 
LEFT JOIN Laptop ON User.id = Laptop.user_id 
WHERE Key.id IS NOT NULL OR Laptop.id IS NOT NULL 
0

Solution un [Edité pour corriger ce que Rashmi Pandit a souligné.]:

SELECT * FROM [User] u 
INNER JOIN [Key] k 
ON u.userid = k.userid 

UNION 

SELECT * FROM [User] u 
INNER JOIN Laptop l 
ON u.userid = l.userid 

[...] 

Solution deux:

SELECT * FROM [User] u 
LEFT JOIN [Key] k 
ON u.userid = k.userid 
LEFT JOIN Laptop l 
ON u.userid = l.userid 
LEFT JOIN [...] 
WHERE k.userid IS NOT NULL 
OR l.userid IS NOT NULL 
OR [...] 

Juste une supposition, aussi vous pouvez vérifier le plan d'exécution pour ces deux pour voir si l'UNION est plus lourd ou vice versa.

2
-- // Assuming that a user can have at max 1 items of each type 
SELECT  u.* 
-- // Assuming that a user can have more then 1 items of each type, just add DISTINCT: 
-- // SELECT  DISTINCT u.* 
FROM  "User" u 
LEFT JOIN "Key" u1 ON u.UserID = u1.UserID 
LEFT JOIN "Laptop" u2 ON u.UserID = u2.UserID 
LEFT JOIN "Server" u3 ON u.UserID = u3.UserID 
-- // ... 
WHERE  COALESCE(u1.UserID, u2.UserID, u3.UserID /*,...*/) IS NOT NULL 
+0

+ 1 utilisation intéressante de coalesce :) – Andomar

Questions connexes