Angenommen, ich möchte einen Benutzer und alle seine Telefonnummern und E-Mail-Adressen abrufen. Die Telefonnummern und E-Mails werden in separaten Tabellen gespeichert, Ein Benutzer zu vielen Telefonen / E-Mails. Das kann ich ganz einfach machen:
SELECT * FROM users user
LEFT JOIN emails email ON email.user_id=user.id
LEFT JOIN phones phone ON phone.user_id=user.id
Das Problem * dabei ist, dass der Name des Benutzers, das DOB, die Lieblingsfarbe und alle anderen Informationen, die in der Benutzertabelle gespeichert sind, für jeden Datensatz immer wieder zurückgegeben werden (Benutzer sendet E- Mail-Telefondatensätze), was vermutlich die Bandbreite verschlingt und verlangsamt die Ergebnisse aufschreiben.
Wäre es nicht schöner, wenn für jeden Benutzer eine einzelne Zeile zurückgegeben würde und in diesem Datensatz eine Liste von E-Mails und eine Liste von Telefonen enthalten wäre? Dies würde die Arbeit mit den Daten erheblich erleichtern.
Ich weiß, dass Sie mit LINQ oder anderen Frameworks solche Ergebnisse erzielen können, aber es scheint eine Schwäche im zugrunde liegenden Design relationaler Datenbanken zu sein.
Wir könnten das mit NoSQL umgehen, aber sollte es keinen Mittelweg geben?
Vermisse ich etwas? Warum gibt es das nicht?
* Ja, es ist so konzipiert. Ich verstehe es. Ich frage mich, warum es keine Alternative gibt, mit der man einfacher arbeiten kann. SQL könnte weitermachen, was es tut, aber dann könnten sie ein oder zwei Schlüsselwörter hinzufügen, um ein bisschen Nachbearbeitung zu machen, die die Daten in einem verschachtelten Format anstelle eines kartesischen Produkts zurückgibt.
Ich weiß, dass dies in einer Skriptsprache Ihrer Wahl durchgeführt werden kann, aber es erfordert, dass der SQL Server entweder redundante Daten sendet (Beispiel unten) oder dass Sie mehrere Abfragen wie ausgeben SELECT email FROM emails WHERE user_id IN (/* result of first query */)
.
Anstatt MySQL so etwas zurückgeben zu lassen:
[
{
"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]",
}
]
Und dann müssen Sie eine eindeutige ID gruppieren (was bedeutet, dass ich diese auch abrufen muss!), Um die Ergebnismenge nach Ihren Wünschen neu zu formatieren. Geben Sie einfach Folgendes zurück:
[
{
"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]"],
}
]
Alternativ kann ich drei Abfragen ausführen: eine für die Benutzer, eine für die E-Mails und eine für die Telefonnummern. In den Ergebnismengen für E-Mail und Telefonnummer muss jedoch die user_id enthalten sein, damit ich sie wieder den Benutzern zuordnen kann Ich habe vorher geholt. Wieder redundante Daten und unnötige Nachbearbeitung.
Antworten:
Tief im Innern einer relationalen Datenbank befinden sich alle Zeilen und Spalten. Das ist die Struktur, mit der eine relationale Datenbank optimiert ist. Cursor bearbeiten jeweils einzelne Zeilen. Einige Operationen erstellen temporäre Tabellen (auch hier müssen es Zeilen und Spalten sein).
Wenn Sie nur mit Zeilen arbeiten und nur Zeilen zurückgeben, kann das System den Speicher- und Netzwerkverkehr besser verarbeiten.
Wie bereits erwähnt, können auf diese Weise bestimmte Optimierungen vorgenommen werden (Indizes, Verknüpfungen, Gewerkschaften usw.).
Wenn man eine verschachtelte Baumstruktur haben möchte, muss man alle Daten auf einmal ziehen. Vorbei sind die Optimierungen für die Cursor auf der Datenbankseite. Ebenso wird der Datenverkehr über das Netzwerk zu einem großen Burst, der viel länger dauern kann als das langsame Hin und Her von Zeile zu Zeile (dies geht in der heutigen Webwelt gelegentlich verloren).
Jede Sprache enthält Arrays. Dies sind einfache Dinge, mit denen man arbeiten und mit denen man interagieren kann. Durch die Verwendung einer sehr primitiven Struktur kann der Treiber zwischen Datenbank und Programm - egal in welcher Sprache - auf eine gemeinsame Weise funktionieren. Sobald man anfängt, Bäume hinzuzufügen, werden die Strukturen in der Sprache komplexer und schwerer zu durchqueren.
Für eine Programmiersprache ist es nicht so schwierig, die zurückgegebenen Zeilen in eine andere Struktur zu konvertieren. Machen Sie daraus einen Baum oder einen Hash-Satz oder lassen Sie ihn als Liste von Zeilen, über die Sie iterieren können.
Hier ist auch Geschichte am Werk. Die Übertragung strukturierter Daten war in früheren Zeiten etwas hässlich. Schauen Sie sich das EDI-Format an, um eine Vorstellung davon zu bekommen, wonach Sie fragen könnten. Bäume implizieren auch Rekursion - was einige Sprachen nicht unterstützten (die beiden wichtigsten Sprachen der alten Zeit unterstützten Rekursion nicht - Rekursion trat erst in Fortran auf, als F90 und in der Ära COBOL ebenfalls nicht).
Und obwohl die heutigen Sprachen Rekursion und fortgeschrittenere Datentypen unterstützen, gibt es keinen guten Grund, etwas zu ändern. Sie arbeiten und sie arbeiten gut. Diejenigen, die Dinge ändern, sind die NOSQL-Datenbanken. Sie können Bäume in Dokumenten in einem dokumentbasierten Dokument speichern. LDAP (es ist eigentlich altmodisch) ist auch ein baumbasiertes System (obwohl es wahrscheinlich nicht das ist, wonach Sie suchen). Wer weiß, vielleicht ist das nächste, was in NOSQL-Datenbanken passiert, eines, das die Abfrage als JSON-Objekt zurückgibt.
Die 'alten' relationalen Datenbanken arbeiten jedoch mit Zeilen, weil sie darin gut sind und alles ohne Probleme oder Übersetzung mit ihnen kommunizieren kann.
Aus RFC 1925 - Die zwölf Wahrheiten der Vernetzung
quelle
Es wird genau das zurückgegeben, wonach Sie gefragt haben: ein einzelner Datensatz, der das durch die Joins definierte kartesische Produkt enthält. Es gibt viele gültige Szenarien, in denen genau das gewünscht wird. Wenn Sie also sagen, dass SQL ein schlechtes Ergebnis liefert (und damit impliziert, dass es besser wäre, wenn Sie es ändern), würden Sie tatsächlich eine Menge Abfragen vermasseln.
Was Sie erleben, ist als " Object / Relational Impedance Mismatch " bekannt, die technischen Schwierigkeiten, die sich aus der Tatsache ergeben, dass sich das objektorientierte Datenmodell und das relationale Datenmodell in verschiedener Hinsicht grundlegend unterscheiden. LINQ und andere Frameworks (bekannt als ORMs, Object / Relational Mappers, nicht zufällig) umgehen dies nicht auf magische Weise. Sie geben nur verschiedene Abfragen aus. Dies kann auch in SQL erfolgen. So würde ich es machen:
Durchlaufen Sie die Liste der Benutzer und erstellen Sie eine Liste der IDs.
Und dann machst du den Beitritt clientseitig. So machen es LINQ und andere Frameworks. Es gibt keine wirkliche Magie. nur eine Abstraktionsebene.
quelle
Sie können eine integrierte Funktion verwenden, um die Datensätze miteinander zu verknüpfen. In MySQL können Sie die
GROUP_CONCAT()
Funktion verwenden und in Oracle können Sie dieLISTAGG()
Funktion verwenden.Hier ist ein Beispiel, wie eine Abfrage in MySQL aussehen könnte:
Dies würde so etwas wie zurückgeben
quelle
Das Problem ist, dass Sie nicht selektiv genug sind. Sie haben um alles gebeten, als Sie sagten
... und Sie haben es bekommen (einschließlich DOB und Lieblingsfarben).
Du solltest wahrscheinlich ein bisschen selektiver sein und so etwas sagen wie:
Es ist auch möglich, dass Sie Datensätze sehen, die wie Duplikate aussehen, da ein
user
Datensatz möglicherweise mit mehrerenemail
Datensätzen verknüpft wird, das Feld, das diese beiden voneinander unterscheidet, jedoch nicht in IhrerSelect
Anweisung enthalten istIch stelle auch fest, dass Sie eine
LEFT JOIN
. Dies verbindet alle Datensätze links vom Join (dhusers
) mit allen Datensätzen rechts, oder mit anderen Worten:( http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join )
Eine andere Frage ist also, ob Sie tatsächlich eine linke Verknüpfung benötigen oder ob eine
INNER JOIN
ausreichend gewesen wäre. Es sind sehr unterschiedliche Arten von Verknüpfungen.Wenn Sie tatsächlich möchten, dass eine einzelne Spalte in der Ergebnismenge eine Liste enthält, die im laufenden Betrieb generiert wird, können Sie dies tun, dies hängt jedoch von der verwendeten Datenbank ab. Oracle hat die
listagg
Funktion .Letztendlich denke ich, dass Ihr Problem gelöst werden könnte , wenn Sie Ihre Abfrage in etwa so umschreiben:
quelle
left join
auf erfolgtinner join
. In diesem Fall werden die "Wiederholungen", über die sich der Benutzer beschwert, nicht verringert. Benutzer, denen ein Telefon oder eine E-Mail fehlt, werden einfach weggelassen. kaum Besserung. Außerdem werden bei der Interpretation von "Alle Datensätze links vor allen Datensätzen rechts" dieON
Kriterien übersprungen , wodurch alle dem kartesischen Produkt innewohnenden "falschen" Beziehungen entfernt werden, aber alle wiederholten Felder beibehalten werden.Abfragen erzeugen immer eine rechteckige (nicht gezackte) tabellarische Datenmenge. Es gibt keine verschachtelten Untermengen innerhalb einer Menge. In der Welt der Mengen ist alles ein reines, nicht verschachteltes Rechteck.
Sie können sich einen Join vorstellen, indem Sie zwei Sätze nebeneinander platzieren. Die Bedingung "Ein" ist, wie die Datensätze in jedem Satz abgeglichen werden. Wenn ein Benutzer 3 Telefonnummern hat, wird in den Benutzerinformationen eine dreimalige Verdoppelung angezeigt. Von der Abfrage muss eine rechteckige, nicht gezackte Menge erstellt werden. Es liegt einfach in der Natur des Verbindens von Sets mit einer 1-zu-viele-Beziehung.
Um das zu bekommen, was Sie wollen, müssen Sie eine separate Abfrage wie die von Mason Wheeler beschriebene verwenden.
Das Ergebnis dieser Abfrage ist immer noch eine rechteckige, nicht gezackte Menge. Wie alles in der Welt der Sets.
quelle
Sie müssen entscheiden, wo die Engpässe bestehen. Die Bandbreite zwischen Ihrer Datenbank und der Anwendung ist normalerweise ziemlich schnell. Es gibt keinen Grund, warum die meisten Datenbanken nicht drei separate Datensätze innerhalb eines Aufrufs und keine Verknüpfungen zurückgeben konnten. Dann können Sie alles in Ihrer App zusammenfügen, wenn Sie möchten.
Andernfalls möchten Sie, dass die Datenbank diesen Datensatz zusammenstellt und dann alle wiederholten Werte in jeder Zeile entfernt, die das Ergebnis der Verknüpfungen sind, und nicht unbedingt, dass die Zeilen selbst doppelte Daten enthalten, z. B. zwei Personen mit demselben Namen oder derselben Telefonnummer. Scheint viel Overhead zu sein, um Bandbreite zu sparen. Sie sollten sich besser darauf konzentrieren, weniger Daten mit einer besseren Filterung zurückzugeben und die nicht benötigten Spalten zu entfernen. Weil Select * nie in der Produktion verwendet wird - das hängt davon ab.
quelle
Verbinden Sie Ihre Daten ganz einfach nicht, wenn Sie eindeutige Ergebnisse für eine Benutzer- und eine Telefonnummernabfrage wünschen. Anderenfalls enthalten die Daten, wie bereits erwähnt, zusätzliche Felder für jede Zeile.
Geben Sie zwei unterschiedliche Abfragen aus, anstatt eine mit einem Join.
In der gespeicherten Prozedur oder in der Inline-Parametrisierung fragt sql craft 2 ab und gibt die Ergebnisse beider zurück. Die meisten Datenbanken und Sprachen unterstützen mehrere Ergebnismengen.
Beispielsweise führen SQL Server und C # diese Funktionen mithilfe von aus
IDataReader.NextResult()
.quelle
Ihnen fehlt etwas. Wenn Sie Ihre Daten denormalisieren möchten, müssen Sie dies selbst tun.
quelle
Das Konzept von relationalen Abschlusses bedeutet im Grunde, dass das Ergebnis jeder Abfrage eine Beziehung ist, die in anderen Abfragen verwendet werden kann, als wäre es eine Basistabelle. Dies ist ein leistungsfähiges Konzept, da es Abfragen zusammensetzbar macht.
Wenn Sie mit SQL Abfragen schreiben könnten, die verschachtelte Datenstrukturen ausgeben, würden Sie dieses Prinzip brechen. Eine verschachtelte Datenstruktur ist keine Relation. Sie benötigen daher eine neue Abfragesprache oder komplexe Erweiterungen für SQL, um sie weiter abzufragen oder um sie mit anderen Relationen zu verknüpfen.
Grundsätzlich würden Sie ein hierarchisches DBMS auf einem relationalen DBMS aufbauen. Für einen zweifelhaften Vorteil wird es viel komplexer, und Sie verlieren die Vorteile eines konsistenten relationalen Systems.
Ich verstehe, warum es manchmal bequem ist, hierarchisch strukturierte Daten aus SQL auszugeben, aber die Kosten für die zusätzliche Komplexität im gesamten DBMS, um dies zu unterstützen, sind es definitiv nicht wert.
quelle
Pls beziehen sich auf die Verwendung der Funktion STUFF, die mehrere Zeilen (Telefonnummern) einer Spalte (Kontakt) gruppiert, die als einzelne Zelle mit begrenzten Werten einer Zeile (Benutzer) extrahiert werden können.
Heute nutzen wir dies ausgiebig, haben jedoch einige Probleme mit der CPU und der Leistung. XML-Datentyp ist eine weitere Option, es handelt sich jedoch um eine Entwurfsänderung und nicht um eine Abfrage der ersten Ebene.
quelle
STUFF
ist ähnlich zu spleißen. Nicht sicher, wie das auf meine Frage zutrifft.