2009-12-23 6 views
1

Je travaille sur une base de données MySQL contenant des personnes. Mon problème est que, (je simplifier pour faire mon point):Problème MySQL simple

J'ai trois tables:

Persons(id int, birthdate date) 
PersonsLastNames(id int, lastname varchar(30)) 
PersonsFirstNames(id int, firstname varchar(30)) 

L'ID est la clé commune. Il existe des tables séparées pour les noms et prénoms car une même personne peut avoir plusieurs prénoms et plusieurs noms de famille.

Je veux faire une requête qui renvoie toutes les personnes avec, disons, un nom de famille. Si je vais avec

select birthdate, lastname, firstname from Persons, PersonsLastNames, 
PersonsFirstNames where Persons.id = PersonsLastNames.id and 
Persons.id = PersonsFirstNames.id and lastName = 'Anderson' 

je me retrouve avec une table comme

1/1/1970 Anderson Steven //Person 1 
1/1/1970 Anderson David //Still Person 1 
2/2/1980 Smith Adam  //Person 2 
3/3/1990 Taylor Ed  //Person 3 

Lors de la présentation, je voudrais avoir

1/1/1970 Anderson Steven David 
2/2/1980 Smith Adam [possibly null?] 
3/3/1990 Taylor Ed [possibly null?] 

Comment puis-je joindre les tables pour introduire de nouvelles colonnes dans le jeu de résultats si nécessaire pour contenir plusieurs prénoms ou noms de famille pour une personne? SQL ne prend pas en charge un nombre dynamique de colonnes dans la liste de sélection de requête.

+0

Quelle est l'application pratique de cela? Quand une personne a-t-elle plusieurs prénoms et noms de famille? Et puisque la relation que vous avez décrite est de plusieurs à plusieurs, le résultat que vous recherchez pourrait avoir n'importe quel nombre de colonnes. – Ken

+0

@Ken - il pourrait exister dans un sens "Alias ​​connus". – Dereleased

+0

Vous pouvez renommer cette question en quelque chose comme "MySQL - Comment retourner des colonnes dynamiques depuis JOIN?" pour aider les futures personnes à la recherche d'un problème similaire. – philfreo

Répondre

1

Votre application vraiment besoin de gérer un nombre illimité de premiers/noms de famille par personne? Je ne connais pas vos besoins spécifiques, mais cela semble être un peu extrême. Indépendamment...

Puisque vous ne pouvez pas vraiment un nombre dynamique de colonnes de retour, vous pourriez faire quelque chose comme ceci:

SELECT birthdate, lastname, GROUP_CONCAT(firstname SEPARATOR '|') AS firstnames 
FROM Persons, PersonsLastNames, PersonsFirstNames 
WHERE Persons.id = PersonsLastNames.id 
AND Persons.id = PersonsFirstNames.id 
GROUP BY Persons.id 

Ce serait retourner une ligne par personne qui a un nom de famille, avec le (illimité) prénoms séparés par un symbole de tuyau (|), fonction GROUP_CONCAT.

birthdate    lastname firstnames 
---     ---  --- 
1970-01-01 00:00:00 Anderson Steven|David 
1980-02-02 00:00:00 Smith  Adam 
1990-03-03 00:00:00 Taylor  Ed 
+1

Oui le GROUP_CONCAT rend la sortie que je veux! Je vous remercie! Qu'en est-il de l'efficacité dans une base de données comptant jusqu'à 8 millions de personnes? –

+0

Je suppose que vous n'aurez pas beaucoup de prénoms par personne, même 10 serait beaucoup, non? Si c'est le cas, je crois que la fonction GROUP_CONCAT n'aura aucun problème de performance, mais plutôt les simples jointures et le groupe qui prendront le temps. Avec les bons indices, je pense que cela devrait être bien. Remarque: veillez à regarder le paramètre 'group_concat_max_len'. – philfreo

1

SQL ne prend pas en charge un nombre dynamique de colonnes dans la liste de sélection de requête. Vous devez définir exactement autant de colonnes que vous le souhaitez (nonobstant le caractère générique *).

Je vous recommande d'aller chercher les noms multiples en tant que lignes, pas de colonnes. Ensuite, écrivez un code d'application pour faire une boucle sur le jeu de résultats et faire ce que vous voulez faire pour les présenter.

1

La réponse courte est, vous ne pouvez pas. Vous devrez toujours choisir un nombre fixe de colonnes. Vous pouvez cependant améliorer considérablement la syntaxe de votre requête en utilisant le mot clé ON. Par exemple:

SELECT 
    birthdate, 
    firstName, 
    lastName 
FROM 
    Persons 
    INNER JOIN PersonsLastNames 
     ON Persons.id = PersonsLastNames.id 
    INNER JOIN PersonsFirstNames 
     ON Persons.id = PersonsFirstNames.id 
WHERE 
    lastName = 'Anderson' 
GROUP BY 
    lastName, firstName 
HAVING 
    count(lastName) = 1 

Bien sûr, ma requête comprend quelques dispositions supplémentaires à la fin de sorte que seules les personnes ayant un seul nom spécifié dernier serait saisi, mais vous pouvez toujours supprimer ceux-ci.

Maintenant, ce que vous pouvez faire est de choisir un maximum de ceux-ci que vous souhaitez récupérer et faire quelque chose comme ceci:

SELECT 
    birthdate, 
    lastName, 
    PersonsFirstNames.firstName, 
    IFNULL(p.firstName,''), 
    IFNULL(q.firstName,'') 
FROM 
    Persons 
    INNER JOIN PersonsLastNames 
     ON Persons.id = PersonsLastNames.id 
    INNER JOIN PersonsFirstNames 
     ON Persons.id = PersonsFirstNames.id 
    LEFT JOIN PersonsFirstNames p 
     ON Persons.id = p.id 
     AND p.firstName <> PersonsFirstNames.firstName 
    LEFT JOIN PersonsFirstNames q 
     ON Persons.id = q.id 
     AND q.firstName <> PersonsFirstNames.firstName 
     AND q.firstName <> p.firstName 
GROUP BY 
    lastName 

Mais je ne recommande vraiment pas. Le mieux est de récupérer plusieurs lignes, puis de les parcourir dans n'importe quelle application que vous utilisez ou développez. Assurez-vous d'avoir lu sur vos types JOIN (Left-vs-Inner), si vous n'êtes pas familier, avant de commencer. J'espère que cela t'aides.

EDIT: Vous pouvez également envisager, dans ce cas, une clause GROUP BY légèrement plus complexe, par ex.

GROUP BY 
    Persons.id, lastName