2010-10-01 3 views
2

Cas généralExécution d'une jointure gauche sur un grand nombre à plusieurs table avec des conditions

Comment effectuer une jointure gauche dans plusieurs-à-plusieurs lorsque vous souhaitez ajouter une condition à l'étranger côté de la relation?

Cas particulier

Nous avons affaire à deux tables: team et pool. Il y a aussi une table team_pool servant de table de jonction plusieurs-à-plusieurs entre eux. De plus, un pool a une colonne stage_id.

Je veux récupérer toutes les équipes avec le groupe de cette équipe pour une étape spécifique. Si le pool n'existe pas, je veux que le pool soit NULL. Exemple, résultats idéalisés:

+--------+----------+------+ 
| team | stage_id | pool | 
+--------+----------+------+ 
| Team 1 |  2 | C | 
| Team 2 |  NULL | NULL | //this team doesn't belong to a pool for this stage (but it could belong to a pool for a different stage) 
| Team 3 |  2 | G | 
| Team 3 |  2 | H | //if a team belongs to a 2 pools for the same stage 
| Team 4 |  2 | D | 

Si c'est pertinent, j'utilise MySQL.

SQL

est ici le (simplifié) SQL utilisée pour créer les tables:

CREATE TABLE team (id BIGINT AUTO_INCREMENT, name VARCHAR(50), PRIMARY KEY(id)); 
CREATE TABLE team_pool (team_id BIGINT, pool_id BIGINT, PRIMARY KEY(team_id, pool_id)); 
CREATE TABLE pool (id BIGINT AUTO_INCREMENT, stage_id BIGINT, name VARCHAR(40) NOT NULL, PRIMARY KEY(id)); 

solution idéale

La solution idéale:

  • Non exiger un ch ange to my schema (ORM le veut vraiment de cette façon)
  • Requiert une seule requête non UNION.

tentatives de solutions

  • Utiliser une jointure interne plutôt qu'une LEFT JOIN team-team_pool et team_pool-pool. Problème: nous perdons des équipes qui ne font pas partie d'une piscine
  • LEFT JOIN team-team_pool et team_pool-pool en utilisant une à la condition que les stage_id sur pool correspond à ce que nous recherchons. Problème: lorsqu'une équipe appartient à plusieurs pools, nous obtenons plusieurs résultats. L'ajout d'un GROUP BY n'aide pas.

EDIT: Solution Elu

Il y a beaucoup de bonnes solutions ici.

Étant donné que ma solution idéale n'est pas possible, je préfère ajouter stage_id à team_pool plutôt que d'utiliser UNION ou des sous-requêtes. Cela a l'avantage supplémentaire de me permettre de faire valoir qu'une équipe ne peut appartenir qu'à un pool par étape. Il rend également les requêtes simples:

SELECT t.name, p.name, tp.stage_id FROM team t LEFT JOIN team_pool tp ON t.id = tp.team_id AND tp.stage_id = 2 LEFT JOIN pool p ON p.id = tp.pool_id 
+0

Comment l'équipe 2 a-t-elle un stage_id si pool est nul? – Snekse

+0

Il ne devrait pas, je l'ai réparé. –

+0

Pouvez-vous ajouter à vos résultats idéalisés pour montrer ce que vous attendez si une équipe appartient à plusieurs piscines? – Snekse

Répondre

1

Si je comprends les concepts derrière votre schéma, alors je pense que stage_id devrait être une colonne dans team_pool plutôt que pool. La scène n'est pas un attribut du pool, c'est un facteur dans la cartographie des équipes en pools, n'est-ce pas? Quoi qu'il en soit, voici comment j'écrirais votre requête dans Oracle. Je ne suis pas sûr que cette syntaxe soit exacte pour MySQL. Vraisemblablement, vous voudriez paramétrer la valeur littérale pour stage_id.

SELECT t.name, p.name 
    FROM (SELECT team.name, pool_id 
      FROM team LEFT JOIN team_pool 
      ON team_pool.team_id = team.team_id) t 
     LEFT JOIN (SELECT pool_id, name FROM pool WHERE stage_id = 2) p 
      ON p.pool_id = t.pool_id 
+0

La scène est un attribut de la piscine (les pools existent pour une certaine étape). Si le choix est entre UNION et l'ajout de stage_id à team_pool, je choisirai ce dernier. La principale raison pour laquelle j'évitais d'ajouter stage_id à team_pool est que l'ORM que j'utilise le rend un peu ennuyeux de le faire. –

1

Réponse courte, il n'est pas possible de répondre à tous vos critères.

Réponse plus longue: il est très possible d'obtenir tous vos résultats avec UNION. La première moitié montrera les équipes qui ont un pool pour cette étape; la seconde montrera les équipes qui ne le font pas.

select ... 
from team 
left join team_pool 
    on team.id = team_pool.team_id 
left-join pool 
    on pool.id = team_pool.pool_id 
     and pool.stage_id = @stage 
UNION 
select distinct team.id, @stage, NULL 
from team 
where not exists 
    (select pool_id 
     from pool 
     inner join team_pool 
     on team_pool.pool_id = pool.pool_id 
     where team_pool.team_id = team.id 
     and pool.stage_id = @stage 
1

Si vous recherchez une seule étape, vous pouvez quitter les tables ensemble. Le nom du pool sera nul si aucun pool n'est trouvé. Par exemple, pour stage = 2:

select t.name, 2, pool.name 
from team t 
left join 
     (
     select team_id, p.name 
     from team_pool tp 
     join pool p 
     on  tp.pool_id = p.id 
     where p.stage_id = 2 
     ) pool 
on  pool.team_id = t.id 

Si vous souhaitez interroger toutes les étapes, vous pouvez utiliser un cross join pour générer une ligne pour chaque combinaison équipe étapes. La jointure gauche recherche ensuite une piscine pour chaque ligne:

select t.name, s.stage_id, p.name 
from team t 
cross join 
     (
     select distinct stage_id 
     from pool 
     ) s 
left join 
     (
     select tp.team_id, p.stage_id, p.name 
     from team_pool tp 
     join pool p 
     on  tp.pool_id = p.id 
     ) pool 
on  pool.team_id = t.id 
     and pool.stage_id = s.stage_id 

Parce que stage_id vient de la jointure croisée, il ne sera pas null si aucune piscine se trouve.

0

Je ne sais pas si cela fonctionnera puisque je n'ai pas de base de données avec laquelle je peux jouer en ce moment, mais je pense que cela devrait fonctionner. Fondamentalement, créez deux "tables" à gauche, puis rejoignez-les à nouveau. Si cela fonctionne, je ne peux pas imaginer que ce serait très performant.

MISE À JOUR simplifié SQL

SELECT t.name, p.stage_id, p.pool_name 
FROM team t 
LEFT JOIN 
    (SELECT pool.name as pool_name, pool.stage_id, team_pool.team_id as junc_team_id 
    FROM pool LEFT JOIN team_pool 
    ON team_pool.pool_id = pool.pool_id) p 
ON p.junc_team_id = t.team_id 
+0

Résumé de SQL: Rejoindre pool et team_pool ensemble pour agir comme une seule table, puis laissé rejoindre l'équipe à cette nouvelle table pseudo en utilisant team_id. Rejoindre pool à team_pool pourrait probablement utiliser un INNER JOIN; c'est simplement un moyen d'associer le team_id au pool_id. Une fois que cela est établi, alors vous devriez pouvoir lier team.team_id à n'importe quel p.team_id sans doublons (je pense). – Snekse

0

Juste pour que nous sommes clairs:

Vous souhaitez fournir une équipe et une étape

Vous voulez retourner: Toutes les équipes partageant une piscine avec votre équipe fournie dans l'étape fournie Toutes les équipes n'ayant pas de piscine dans le stage fourni combo piscine/équipe pour toute équipe partageant un pool avec votre équipe fournie dans l'étape prévue?

Je suis assez sûr que cela peut être accompli sans UNION mais je ne suis pas tout à fait clair sur ce que les résultats que vous êtes après

En entrée, l'OMI:

Votre première sélection devrait être sur la table des équipes

vous devez ajouter des critères pour la piscine dans la clause JOIN plutôt que la clause WHERE

vous devez joindre à nouveau les équipes table une fois que vous avez désiré étape pour présenter tous les pools de cette équipe

Questions connexes