2009-06-10 8 views
0

J'ai ces tables:Jointure multiple SQL sur de nombreuses tables + séparation de virgules

table de média - clé primaire id int, uri varchar.
media_to_people - media_id int clé primaire, people_id int clé primaire
personnes - id int clé primaire, nom varchar, rôle int - rôle spécifie si la personne est un artiste, un éditeur, un écrivain, un acteur, etc relatif aux médias et a une portée (1-10)

C'est plusieurs à plusieurs

Je veux chercher un média et toutes ses personnes associées à une sélection. Donc, si un média a 10 personnes associées, tous les 10 doivent venir.

De plus, si plusieurs personnes avec le même rôle existe pour un média donné, ils doivent venir sous forme de valeurs séparées par des virgules dans une colonne pour ce rôle.

rubriques de résultat doit ressembler à: media.id, media.uri, people.name (acteur), people.name (artiste), people.name (éditeur) et ainsi de suite.

J'utilise SQLite.

Répondre

3

Je suis d'accord avec Alex Martelli answer, que vous devriez obtenir les données sur plusieurs lignes et faire un peu de traitement dans votre application. Si vous tentez de le faire uniquement avec des jointures, vous devez vous connecter à la table des personnes pour chaque type de rôle. S'il y a plusieurs personnes dans chaque rôle, votre requête aura des produits cartésiens entre ces rôles.

Vous avez donc besoin de le faire avec GROUP_CONCAT() et produire un scalaire dans votre sous-requête liste de sélection pour chaque rôle:

SELECT m.id, m.uri, 
(SELECT GROUP_CONCAT(name) 
    FROM media_to_people JOIN people ON (people_id = id) 
    WHERE media_id = m.id AND role = 1) AS Actors, 
(SELECT GROUP_CONCAT(name) 
    FROM media_to_people JOIN people ON (people_id = id) 
    WHERE media_id = m.id AND role = 2) AS Artists, 
(SELECT GROUP_CONCAT(name) 
    FROM media_to_people JOIN people ON (people_id = id) 
    WHERE media_id = m.id AND role = 3) AS Publishers 
FROM media m; 

C'est vraiment laid! N'essayez pas ca a la maison!

Suivez notre conseil et n'essayez pas de formater le tableau croisé dynamique en utilisant uniquement SQL.

+0

Cela fonctionne parfaitement! C'est exactement ce dont j'ai besoin! Est-ce que ça frappe la performance? – jetru

+1

Oui! Il frappe la performance dur !! C'est pourquoi je t'ai dit de ne pas le faire! En plus d'être moche, le code ne peut pas être maintenu. –

7

SQLite n'a pas la fonctionnalité "pivot" dont vous auriez besoin pour les démarreurs, et la partie "valeurs séparées par des virgules" est définitivement un problème de présentation qu'il serait absurde (et peut-être irréalisable) d'essayer de pousser couche de base de données, quel que soit le dialecte de SQL peut être impliqué - c'est certainement une partie du travail que vous feriez dans le client, par exemple un outil de reporting ou un langage de programmation.

Utilisez SQL pour données et laissez la présentation à d'autres couches.

Comment vous obtenez vos données est

SELECT media.id, media.uri, people.name, people.role 
FROM media 
JOIN media_to_people ON (media.id = media_to_people.media_id) 
JOIN people ON (media_to_people.people_id = people.id) 
WHERE media.id = ? 
ORDER BY people.role, people.name 

(le? Est une façon d'indiquer un paramètre dans SQLite, être lié à l'identifiant spécifique de média que vous êtes à la recherche de façons qui dépendent de votre client) les données proviendront de la base de données vers votre code client sur plusieurs lignes et votre code client peut facilement les placer dans le formulaire à colonne unique souhaité.

Il est difficile pour nous de dire comment coder la partie côté client w/o savoir quoi que ce soit au sujet de l'environnement ou de la langue que vous utilisez en tant que client. Mais en Python par exemple:

def showit(dataset): 
    by_role = collections.defaultdict(list) 
    for mediaid, mediauri, name, role in dataset: 
    by_role[role].append(name) 
    headers = ['mediaid', 'mediauri'] 
    result = [mediaid, mediauri] 
    for role in sorted(by_role): 
    headers.append('people(%s)' % role) 
    result.append(','.join(by_role[role])) 
    return ' '.join(headers) + '\n' + ' '.join(result) 

même ce ne correspond pas tout à fait vos spécifications - vous demandez des en-têtes tels que « les gens (artiste) » alors que vous spécifiez également codé du rôle d'int, et mention aucun moyen d'aller de l'int à la chaîne « artiste », il est évidemment impossible de faire correspondre vos spécifications exactement ... mais il est aussi proche que mon ingéniosité peut obtenir ;-).

+0

+1 pour recommander le post-traitement des lignes dans une langue d'application client! –

+0

Hmm, il n'y a donc aucun moyen d'aller chercher les colonnes people.name spécifiquement pour les différents rôles? Je pensais quelque chose comme ceci: SELECT media.id, media.uri, artists.name, publishers.name DE médias JOIN id = media_to_people.people_id ET artists.role = 2) OR (publishers.role = 8 AND publishers.id = media_to_people.people_id) Mais cela ne fonctionne pas comme prévu ... – jetru

+0

Eh bien, le nom de l'en-tête n'est pas si important comme l'obtenir dans une rangée. Je veux que tout soit fait en une seule requête. J'écris C. Vous pouvez imaginer pourquoi je veux éviter le post-traitement de mes résultats. ;) – jetru

Questions connexes