Par exemple, disons que je veux récupérer un utilisateur et tous ses numéros de téléphone et adresses e-mail. Les numéros de téléphone et les e-mails sont stockés dans des tableaux séparés, un utilisateur pour plusieurs téléphones / e-mails. Je peux le faire assez facilement:
SELECT * FROM users user
LEFT JOIN emails email ON email.user_id=user.id
LEFT JOIN phones phone ON phone.user_id=user.id
Le problème * avec cela est qu'il renvoie le nom de l'utilisateur, la date de naissance, la couleur préférée et toutes les autres informations stockées dans la table utilisateur encore et encore pour chaque enregistrement (les utilisateurs envoient des enregistrements par téléphone), ce qui suppose vraisemblablement une bande passante et un ralentissement. les résultats.
Ne serait-il pas plus agréable de renvoyer une seule ligne pour chaque utilisateur, et dans cet enregistrement, il y avait une liste de courriels et une liste de téléphones? Cela rendrait également les données beaucoup plus faciles à utiliser.
Je sais que vous pouvez obtenir des résultats comme celui-ci en utilisant LINQ ou peut-être d'autres cadres, mais cela semble être une faiblesse dans la conception sous-jacente des bases de données relationnelles.
Nous pourrions contourner cela en utilisant NoSQL, mais ne devrait-il pas y avoir un juste milieu?
Suis-je en train de manquer quelque chose? Pourquoi cela n'existe-t-il pas?
* Oui, c'est conçu de cette façon. J'ai compris. Je me demande pourquoi il n'y a pas d'alternative plus facile à utiliser. SQL pourrait continuer à faire ce qu'il fait, mais ensuite ils pourraient ajouter un ou deux mots clés pour faire un peu de post-traitement qui renvoie les données dans un format imbriqué au lieu d'un produit cartésien.
Je sais que cela peut être fait dans un langage de script de votre choix, mais cela nécessite que le serveur SQL envoie des données redondantes (exemple ci-dessous) ou que vous émettiez plusieurs requêtes comme SELECT email FROM emails WHERE user_id IN (/* result of first query */)
.
Au lieu d'avoir MySQL retourner quelque chose de semblable à ceci:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "[email protected]",
},
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "[email protected]",
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"email": "[email protected]",
}
]
Et puis avoir à regrouper sur un identifiant unique (ce qui signifie que je dois aussi le récupérer!) Côté client pour reformater le jeu de résultats comme vous le souhaitez, il suffit de retourner ceci:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"emails": ["[email protected]", "[email protected]"]
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"emails": ["[email protected]"],
}
]
Alternativement, je peux émettre 3 requêtes: 1 pour les utilisateurs, 1 pour les e-mails et 1 pour les numéros de téléphone, mais les ensembles de résultats de courrier électronique et de numéro de téléphone doivent contenir le user_id afin que je puisse les faire correspondre avec les utilisateurs J'ai déjà récupéré. Encore une fois, des données redondantes et un post-traitement inutile.
Réponses:
Au fond, dans les entrailles d'une base de données relationnelle, toutes ses lignes et colonnes. C'est la structure avec laquelle une base de données relationnelle est optimisée pour fonctionner. Les curseurs travaillent sur des lignes individuelles à la fois. Certaines opérations créent des tables temporaires (encore une fois, il doit s'agir de lignes et de colonnes).
En ne travaillant qu'avec des lignes et en ne renvoyant que des lignes, le système peut mieux gérer la mémoire et le trafic réseau.
Comme mentionné, cela permet d'effectuer certaines optimisations (index, jointures, unions, etc ...)
Si l'on voulait une structure arborescente imbriquée, cela nécessite que l'on tire toutes les données à la fois. Finies les optimisations pour les curseurs côté base de données. De même, le trafic sur le réseau devient une grande rafale qui peut prendre beaucoup plus de temps que le lent filet de ligne par ligne (c'est quelque chose qui est parfois perdu dans le monde Web d'aujourd'hui).
Chaque langue contient des tableaux. Ce sont des choses faciles à travailler et à interfacer. En utilisant une structure très primitive, le pilote entre la base de données et le programme - quelle que soit la langue - peut fonctionner de manière courante. Une fois que l'on commence à ajouter des arbres, les structures du langage deviennent plus complexes et plus difficiles à parcourir.
Ce n'est pas si difficile pour un langage de programmation de convertir les lignes retournées dans une autre structure. Faites-en un arbre ou un ensemble de hachage ou laissez-le comme une liste de lignes que vous pouvez parcourir.
Il y a aussi de l'histoire à l'œuvre ici. Transférer des données structurées était quelque chose de laid à l'époque. Regardez le format EDI pour avoir une idée de ce que vous pourriez demander. Les arbres impliquent également la récursivité - que certaines langues ne prenaient pas en charge (les deux langues les plus importantes de l'ancien temps ne prenaient pas en charge la récursivité - la récursivité n'est entrée dans Fortran qu'en F90 et à l'époque COBOL non plus).
Et bien que les langues d'aujourd'hui prennent en charge la récursivité et les types de données plus avancés, il n'y a pas vraiment de bonne raison de changer les choses. Ils fonctionnent et ils fonctionnent bien. Ceux qui sont en train de changer les choses sont les bases de données NoSQL. Vous pouvez stocker des arborescences dans des documents dans un document basé sur un. LDAP (c'est en fait ancien) est également un système basé sur un arbre (bien que ce ne soit probablement pas ce que vous recherchez). Qui sait, peut-être que la prochaine chose dans les bases de données nosql sera de retourner la requête en tant qu'objet json.
Cependant, les «anciennes» bases de données relationnelles ... elles fonctionnent avec des lignes parce que c'est leur domaine et tout peut leur parler sans problème ni traduction.
De RFC 1925 - Les douze vérités de mise en réseau
la source
Il renvoie exactement ce que vous avez demandé: un seul jeu d'enregistrements contenant le produit cartésien défini par les jointures. Il existe de nombreux scénarios valides où c'est exactement ce que vous voudriez, donc dire que SQL donne un mauvais résultat (et donc impliquer qu'il serait préférable que vous le changiez) reviendrait en fait à bousiller beaucoup de requêtes.
Ce que vous rencontrez est connu sous le nom de «non -concordance d'impédance objet / relationnelle », les difficultés techniques qui découlent du fait que le modèle de données orienté objet et le modèle de données relationnel sont fondamentalement différents à plusieurs égards. LINQ et les autres frameworks (connus sous le nom d'ORM, Object / Relational Mappers, pas par coïncidence) ne sont pas comme par magie "contourner cela;" ils émettent simplement des requêtes différentes. Cela peut aussi être fait en SQL. Voici comment je le ferais:
Itérer la liste des utilisateurs et faire une liste d'ID.
Et ensuite, vous vous joignez au côté client. C'est ainsi que LINQ et d'autres frameworks le font. Il n'y a pas de vraie magie impliquée; juste une couche d'abstraction.
la source
Vous pouvez utiliser une fonction intégrée pour concaténer les enregistrements ensemble. Dans MySQL, vous pouvez utiliser la
GROUP_CONCAT()
fonction et dans Oracle, vous pouvez utiliser laLISTAGG()
fonction.Voici un exemple de ce à quoi pourrait ressembler une requête dans MySQL:
Cela retournerait quelque chose comme
la source
Le problème est que vous n'êtes pas assez sélectif. Vous avez demandé tout quand vous avez dit
... et vous l'avez compris (y compris la date de naissance et les couleurs préférées).
Vous devriez probablement être un peu plus (hum) ... sélectif, et dire quelque chose comme:
Il est également possible que vous voyiez des enregistrements qui ressemblent à des doublons car un
user
peut se joindre à plusieursemail
enregistrements, mais le champ qui les distingue n'est pas dans votreSelect
déclaration, vous pouvez donc vouloir dire quelque chose commeDe plus, je remarque que vous faites un
LEFT JOIN
. Cela joindra tous les enregistrements à gauche de la jointure (c'estusers
-à- dire ) à tous les enregistrements à droite, ou en d'autres termes:( http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join )
Une autre question est donc la suivante: avez-vous réellement besoin d' une jointure gauche ou aurait
INNER JOIN
-elle suffi? Ce sont des types de jointures très différents.Si vous voulez réellement qu'une seule colonne dans le jeu de résultats contienne une liste qui est générée à la volée, cela peut être fait mais cela varie en fonction de la base de données que vous utilisez. Oracle a la
listagg
fonction .En fin de compte, je pense que votre problème pourrait être résolu si vous réécrivez votre requête près de quelque chose comme ceci:
la source
left join
àinner join
. Dans ce cas, cela ne réduira pas les «répétitions» dont se plaint l'utilisateur; cela supprimerait simplement les utilisateurs qui n'ont ni téléphone ni e-mail. pratiquement aucune amélioration. en outre, lors de l'interprétation de «tous les enregistrements de gauche à tous les enregistrements de droite», leON
critère est ignoré , qui élague toutes les «mauvaises» relations inhérentes au produit cartésien mais conserve tous les champs répétés.Les requêtes produisent toujours un ensemble tabulaire rectangulaire (non dentelé) de données. Il n'y a pas de sous-ensembles imbriqués dans un ensemble. Dans le monde des décors, tout est un pur rectangle non imbriqué.
Vous pouvez considérer une jointure comme mettant 2 ensembles côte à côte. La condition "on" est la façon dont les enregistrements de chaque ensemble sont mis en correspondance. Si un utilisateur possède 3 numéros de téléphone, vous verrez une duplication 3 fois dans les informations utilisateur. Un ensemble rectangulaire non dentelé doit être produit par la requête. C'est simplement la nature de joindre des ensembles avec une relation 1 à plusieurs.
Pour obtenir ce que vous voulez, vous devez utiliser une requête distincte comme Mason Wheeler décrit.
Le résultat de cette requête est toujours un ensemble rectangulaire non dentelé. Comme tout dans le monde des décors.
la source
Vous devez décider où les goulots d'étranglement existent. La bande passante entre votre base de données et votre application est généralement assez rapide. Il n'y a aucune raison pour laquelle la plupart des bases de données ne peuvent pas renvoyer 3 jeux de données distincts au cours d'un même appel et aucune jointure. Ensuite, vous pouvez tout regrouper dans votre application si vous le souhaitez.
Sinon, vous voulez que la base de données rassemble cet ensemble de données, puis supprime toutes les valeurs répétées dans chaque ligne qui sont le résultat des jointures et pas nécessairement les lignes elles-mêmes ayant des données en double comme deux personnes avec le même nom ou numéro de téléphone. On dirait beaucoup de frais généraux pour économiser sur la bande passante. Vous feriez mieux de vous concentrer sur le retour de moins de données avec un meilleur filtrage et la suppression des colonnes dont vous n'avez pas besoin. Parce que Select * n'est jamais utilisé en production, cela dépend.
la source
Très simplement, ne joignez pas vos données si vous voulez des résultats distincts pour une requête utilisateur et une requête de numéro de téléphone, sinon, comme d'autres l'ont souligné, le "Set" ou les données contiendront des champs supplémentaires pour chaque ligne.
Émettez 2 requêtes distinctes au lieu d'une avec une jointure.
Dans la procédure stockée ou SQL paramétré en ligne, 2 requêtes et renvoyez les résultats des deux. La plupart des bases de données et des langues prennent en charge plusieurs jeux de résultats.
Par exemple, SQL Server et C # accomplissent cette fonctionnalité en utilisant
IDataReader.NextResult()
.la source
Vous manquez quelque chose. Si vous souhaitez dénormaliser vos données, vous devez le faire vous-même.
la source
Le concept de fermeture relationnelle signifie essentiellement que le résultat de toute requête est une relation qui peut être utilisée dans d'autres requêtes comme s'il s'agissait d'une table de base. Il s'agit d'un concept puissant car il rend les requêtes composables.
Si SQL vous permettait d'écrire des requêtes qui produisent des structures de données imbriquées, vous briseriez ce principe. Une structure de données imbriquée n'est pas une relation, vous auriez donc besoin d'un nouveau langage de requête ou d'extensions complexes pour SQL, afin de l'interroger davantage ou de la joindre à d'autres relations.
Fondamentalement, vous construisez un SGBD hiérarchique au-dessus d'un SGBD relationnel. Ce sera beaucoup plus complexe pour un bénéfice douteux et vous perdrez les avantages d'un système relationnel cohérent.
Je comprends pourquoi il serait parfois pratique de pouvoir sortir des données structurées hiérarchiquement à partir de SQL, mais le coût de la complexité supplémentaire dans le SGBD pour prendre en charge cela n'en vaut certainement pas la peine.
la source
Les pls font référence à l'utilisation de la fonction STUFF qui regroupe plusieurs lignes (numéros de téléphone) d'une colonne (contact) qui peuvent être extraites comme une seule cellule de valeurs délimitées d'une ligne (utilisateur).
Aujourd'hui, nous l'utilisons largement, mais nous sommes confrontés à des problèmes de processeur et de performances élevés. Le type de données XML est une autre option, mais il s'agit d'un changement de conception et non d'un niveau de requête.
la source
STUFF
ressemble à une épissure. Je ne sais pas comment cela s'applique à ma question.