Was ist SQL? Die Verkehrssprache der Datenanalyse
Heutzutage ist die strukturierte Abfragesprache das Standardmittel zum Bearbeiten und Abfragen von Daten in relationalen Datenbanken, allerdings mit proprietären Erweiterungen unter den Produkten. Die Leichtigkeit und Allgegenwart von SQL hat sogar dazu geführt, dass die Entwickler vieler „NoSQL“ - oder nicht relationaler Datenspeicher wie Hadoop Teilmengen von SQL übernommen oder eigene SQL-ähnliche Abfragesprachen entwickelt haben.
SQL war jedoch nicht immer die „universelle“ Sprache für relationale Datenbanken. Von Anfang an (ca. 1980) hatte SQL gewisse Streiks dagegen. Viele Forscher und Entwickler zu dieser Zeit, einschließlich mir, dachten, dass der Overhead von SQL verhindern würde, dass es jemals in einer Produktionsdatenbank praktikabel wird.
Wir haben uns eindeutig geirrt. Viele glauben jedoch immer noch, dass der Preis für die Laufzeitleistung bei aller Einfachheit und Zugänglichkeit von SQL oft zu hoch ist.
SQL-Verlauf
Bevor es SQL gab, hatten Datenbanken enge Navigationsprogrammierschnittstellen und wurden normalerweise um ein Netzwerkschema herum entworfen, das als CODASYL-Datenmodell bezeichnet wird. CODASYL (Ausschuss für Datensystemsprachen) war ein Konsortium, das für die Programmiersprache COBOL (ab 1959) und die Erweiterung der Datenbanksprache (ab 10 Jahre später) verantwortlich war.
Wenn Sie für eine CODASYL-Datenbank programmiert haben, haben Sie über Sätze, die Eins-zu-Viele-Beziehungen ausdrücken, zu Datensätzen navigiert. Ältere hierarchische Datenbanken erlauben nur, dass ein Datensatz zu einem Satz gehört. In Netzwerkdatenbanken kann ein Datensatz zu mehreren Gruppen gehören.
Angenommen, Sie möchten die in CS 101 eingeschriebenen Schüler auflisten. Zuerst finden Sie "CS 101"
in der Courses
Gruppe nach Namen, legen fest, dass Sie als Eigentümer oder Eltern der Enrollees
Gruppe das erste Mitglied ( ffm
) der Enrollees
Gruppe finden, das ein Student
Datensatz ist, und listen es. Dann würden Sie in eine Schleife gehen: Finden Sie das nächste Mitglied ( fnm
) und listen Sie es auf. Wenn dies fnm
fehlschlägt, verlassen Sie die Schleife.
Das mag für den Datenbankprogrammierer viel Arbeit bedeuten, war aber zur Ausführungszeit sehr effizient. Experten wie Michael Stonebraker von der University of California in Berkeley und Ingres wiesen darauf hin, dass diese Art der Abfrage in einer CODASYL-Datenbank wie IDMS ungefähr die Hälfte der CPU-Zeit und weniger als die Hälfte des Arbeitsspeichers in derselben relationalen Datenbank mit SQL in Anspruch nahm .
Zum Vergleich wäre die entsprechende SQL-Abfrage, mit der alle Schüler in CS 101 zurückgegeben werden, ungefähr so
SELECT student.name FROM Kurse, Teilnehmer, Studenten WHERE Kursname
Diese Syntax impliziert einen relationalen inneren Join (tatsächlich zwei davon), wie ich weiter unten erläutern werde, und lässt einige wichtige Details aus, wie z. B. die für die Joins verwendeten Felder.
Relationale Datenbanken und SQL
Warum sollten Sie die Verbesserung der Ausführungsgeschwindigkeit und der Speichernutzung um den Faktor zwei aufgeben? Es gab zwei große Gründe: einfache Entwicklung und Portabilität. Ich dachte nicht, dass einer von beiden 1980 im Vergleich zu Leistung und Speicherbedarf eine große Rolle spielte, aber als sich die Computerhardware verbesserte und billiger wurde, kümmerten sich die Leute nicht mehr um Ausführungsgeschwindigkeit und Speicher und machten sich mehr Sorgen um die Entwicklungskosten.
Mit anderen Worten, Moores Gesetz hat CODASYL-Datenbanken zugunsten relationaler Datenbanken getötet. Die Verbesserung der Entwicklungszeit war zwar erheblich, aber die SQL-Portabilität erwies sich als Wunschtraum.
Woher kamen das relationale Modell und SQL? EF „Ted“ Codd war ein Informatiker am IBM San Jose Research Laboratory, der in den 1960er Jahren die Theorie des relationalen Modells erarbeitete und 1970 veröffentlichte. IBM implementierte nur langsam eine relationale Datenbank, um die Einnahmen von zu schützen seine CODASYL-Datenbank IMS / DB. Als IBM schließlich sein System R-Projekt startete, war das Entwicklungsteam (Don Chamberlin und Ray Boyce) nicht unter Codd und ignorierte Codds relationales Alpha-Sprachpapier von 1971, um ihre eigene Sprache, SEQUEL (Structured English Query Language), zu entwerfen. Im Jahr 1979, bevor IBM sein Produkt überhaupt veröffentlicht hatte, nahm Larry Ellison die Sprache in seine Oracle-Datenbank auf (unter Verwendung der SEQUEL-Veröffentlichungen vor dem Start von IBM als Spezifikation). SEQUEL wurde bald zu SQL, um eine internationale Markenverletzung zu vermeiden.
Die „Tom-Toms, die für SQL schlagen“ (wie Michael Stonebraker es ausdrückte) kamen nicht nur von Oracle und IBM, sondern auch von Kunden. Es war nicht einfach, CODASYL-Datenbankdesigner und -programmierer einzustellen oder zu schulen, daher sahen SEQUEL (und SQL) viel attraktiver aus. SQL war in den späten 1980er Jahren so attraktiv, dass viele Datenbankanbieter zum großen Entsetzen von Codd, der der Ansicht war, dass relationale Datenbanken von Grund auf neu entworfen werden mussten, um relational zu sein, im Wesentlichen einen SQL-Abfrageprozessor über ihre CODASYL-Datenbanken hefteten.
Eine reine relationale Datenbank, wie sie von Codd entworfen wurde, basiert auf Tupeln, die in Relationen gruppiert sind und mit der Prädikatenlogik erster Ordnung übereinstimmen. In relationalen Datenbanken der realen Welt gibt es Tabellen, die Felder, Einschränkungen und Trigger enthalten, und Tabellen werden über Fremdschlüssel verknüpft. SQL wird verwendet, um die zurückzugebenden Daten zu deklarieren, und ein SQL-Abfrageprozessor und ein Abfrageoptimierer verwandeln die SQL-Deklaration in einen Abfrageplan, der vom Datenbankmodul ausgeführt wird.
SQL enthält eine Subsprache zum Definieren von Schemas, die Datendefinitionssprache (DDL), sowie eine Subsprache zum Ändern von Daten, die Datenmanipulationssprache (DML). Beide haben Wurzeln in frühen CODASYL-Spezifikationen. Die dritte Subsprache in SQL deklariert Abfragen über die SELECT
Anweisung und relationale Verknüpfungen.
SQL- SELECT
Anweisung
Die SELECT
Anweisung teilt dem Abfrageoptimierer mit, welche Daten zurückgegeben werden sollen, in welchen Tabellen gesucht werden soll, welche Beziehungen zu befolgen sind und in welcher Reihenfolge die zurückgegebenen Daten festgelegt werden sollen. Das Abfrageoptimierungsprogramm muss selbst herausfinden, welche Indizes verwendet werden sollen, um Brute-Force-Tabellenscans zu vermeiden und eine gute Abfrageleistung zu erzielen, es sei denn, die jeweilige Datenbank unterstützt Indexhinweise.
Ein Teil der Kunst des relationalen Datenbankdesigns hängt von der vernünftigen Verwendung von Indizes ab. Wenn Sie einen Index für eine häufige Abfrage weglassen, kann die gesamte Datenbank bei starker Leselast langsamer werden. Wenn Sie zu viele Indizes haben, kann die gesamte Datenbank unter starken Schreib- und Aktualisierungslasten langsamer werden.
Eine weitere wichtige Kunst ist die Auswahl eines guten, einzigartigen Primärschlüssels für jeden Tisch. Sie müssen nicht nur die Auswirkungen des Primärschlüssels auf allgemeine Abfragen berücksichtigen, sondern auch, wie er in Verknüpfungen abgespielt wird, wenn er als Fremdschlüssel in einer anderen Tabelle angezeigt wird, und wie er sich auf die Referenzlokalität der Daten auswirkt.
Im erweiterten Fall von Datenbanktabellen, die je nach Wert des Primärschlüssels in verschiedene Volumes aufgeteilt werden (horizontales Sharding), müssen Sie auch berücksichtigen, wie sich der Primärschlüssel auf das Sharding auswirkt. Hinweis: Sie möchten, dass die Tabelle gleichmäßig über die Volumes verteilt ist. Dies bedeutet, dass Sie keine Datumsstempel oder aufeinanderfolgende Ganzzahlen als Primärschlüssel verwenden möchten.
Diskussionen über die SELECT
Aussage mögen einfach beginnen, können aber schnell verwirrend werden. Erwägen:
SELECT * FROM Kunden;
Einfach, richtig? Es werden alle Felder und Zeilen der Customers
Tabelle abgefragt. Angenommen, die Customers
Tabelle enthält hundert Millionen Zeilen und hundert Felder, und eines der Felder ist ein großes Textfeld für Kommentare. Wie lange dauert es, bis alle Daten über eine Netzwerkverbindung mit 10 Megabit pro Sekunde abgerufen werden, wenn jede Zeile durchschnittlich 1 Kilobyte Daten enthält?
Vielleicht sollten Sie reduzieren, wie viel Sie über den Draht senden. Erwägen:
SELECT TOP 100 Firmenname, lastSaleDate, lastSaleAmount, totalSalesAmount FROM KundenWO Staat UND Stadt
ORDER BY lastSaleDate DESCENDING;
Jetzt werden Sie viel weniger Daten abrufen. Sie haben die Datenbank gebeten, nur vier Felder anzugeben, nur die Unternehmen in Cleveland zu berücksichtigen und nur die 100 Unternehmen mit den letzten Verkäufen anzugeben. Um dies auf dem Datenbankserver am effizientesten zu tun, Customers
benötigt die Tabelle jedoch einen Index state+city
für die WHERE
Klausel und einen Index lastSaleDate
für die Klauseln ORDER BY
und TOP 100
.
Gilt übrigens TOP 100
für SQL Server und SQL Azure, nicht jedoch für MySQL oder Oracle. In MySQL würden Sie LIMIT 100
nach der WHERE
Klausel verwenden. In Oracle würden Sie eine Bindung ROWNUM
als Teil der WHERE
Klausel verwenden, d WHERE... AND ROWNUM <=100
. H. Leider gehen die ANSI / ISO-SQL-Standards (und es gibt bisher neun davon, die sich von 1986 bis 2016 erstrecken) nur so weit, dass jede Datenbank ihre eigenen proprietären Klauseln und Funktionen einführt.
SQL-Joins
Bisher habe ich die SELECT
Syntax für einzelne Tabellen beschrieben. Bevor ich JOIN
Klauseln erklären kann , müssen Sie Fremdschlüssel und Beziehungen zwischen Tabellen verstehen. Ich werde dies anhand von Beispielen in DDL unter Verwendung der SQL Server-Syntax erklären.
Die Kurzversion davon ist ziemlich einfach. Jede Tabelle, die Sie in Beziehungen verwenden möchten, sollte eine Primärschlüsseleinschränkung haben. Dies kann entweder ein einzelnes Feld oder eine Kombination von Feldern sein, die durch einen Ausdruck definiert sind. Zum Beispiel:
TABELLE ERSTELLEN Personen (PersonID int NICHT NULL PRIMARY KEY,
PersonName char (80),
...
Jede Tabelle, auf die Bezug genommen werden muss, Persons
sollte ein Feld haben, das dem Persons
Primärschlüssel entspricht. Um die relationale Integrität zu gewährleisten, sollte dieses Feld eine Fremdschlüsseleinschränkung aufweisen. Zum Beispiel:
TABELLE ERSTELLEN Bestellungen (OrderID int NICHT NULL PRIMARY KEY,
...
PersonID int AUSLÄNDISCHE SCHLÜSSELREFERENZEN Personen (PersonID)
);
Es gibt längere Versionen beider Anweisungen, die das CONSTRAINT
Schlüsselwort verwenden, mit dem Sie die Einschränkung benennen können. Das ist es, was die meisten Datenbank-Design-Tools generieren.
Primärschlüssel sind immer indiziert und eindeutig (die Feldwerte können nicht dupliziert werden). Andere Felder können optional indiziert werden. Es ist oft nützlich, Indizes für Fremdschlüsselfelder und für Felder zu erstellen, die in WHERE
und ORDER BY
Klauseln erscheinen, obwohl dies nicht immer der Fall ist, da der Schreib- und Aktualisierungsaufwand potenziell hoch ist.
Wie würden Sie eine Abfrage schreiben, die alle von John Doe erteilten Bestellungen zurückgibt?
SELECT PersonName, OrderID FROM PersonsINNER JOIN Orders ON Persons.PersonID = Orders.PersonID
WO PersonName;
In der Tat gibt es vier Arten von JOIN
: INNER
, OUTER
, LEFT
, und RIGHT
. Dies INNER JOIN
ist die Standardeinstellung (Sie können das Wort weglassen INNER
) und enthält nur Zeilen, die in beiden Tabellen übereinstimmende Werte enthalten. Wenn Sie Personen auflisten möchten, ob sie Bestellungen haben oder nicht, verwenden Sie beispielsweise Folgendes LEFT JOIN
:
SELECT PersonName, OrderID FROM PersonsLEFT JOIN Orders ON Persons.PersonID = Orders.PersonID
ORDER BY PersonName;
Wenn Sie Abfragen ausführen, die mehr als zwei Tabellen verknüpfen, Ausdrücke verwenden oder Datentypen erzwingen, kann die Syntax zunächst etwas haarig werden. Glücklicherweise gibt es Datenbankentwicklungstools, mit denen Sie korrekte SQL-Abfragen generieren können, häufig durch Ziehen und Ablegen von Tabellen und Feldern aus dem Schemadiagramm in ein Abfragediagramm.
Gespeicherte SQL-Prozeduren
Manchmal SELECT
bringt Sie der deklarative Charakter der Aussage nicht dahin, wohin Sie wollen. Die meisten Datenbanken verfügen über eine Funktion, die als gespeicherte Prozeduren bezeichnet wird. Leider ist dies ein Bereich, in dem fast alle Datenbanken proprietäre Erweiterungen der ANSI / ISO-SQL-Standards verwenden.
In SQL Server war der ursprüngliche Dialekt für gespeicherte Prozeduren (oder gespeicherte Prozesse) Transact-SQL, auch bekannt als T-SQL. in Oracle war es PL-SQL. Beide Datenbanken haben zusätzliche Sprachen für gespeicherte Prozeduren wie C #, Java und R hinzugefügt. Eine einfache gespeicherte T-SQL-Prozedur ist möglicherweise nur eine parametrisierte Version einer SELECT
Anweisung. Seine Vorteile sind Benutzerfreundlichkeit und Effizienz. Gespeicherte Prozeduren werden beim Speichern optimiert, nicht jedes Mal, wenn sie ausgeführt werden.
Eine kompliziertere gespeicherte T-SQL-Prozedur verwendet möglicherweise mehrere SQL-Anweisungen, Eingabe- und Ausgabeparameter, lokale Variablen, BEGIN...END
Blöcke, IF...THEN...ELSE
Bedingungen, Cursor (zeilenweise Verarbeitung einer Menge), Ausdrücke, temporäre Tabellen und eine ganze Reihe anderer prozedurale Syntax. Wenn die Sprache der gespeicherten Prozedur C #, Java oder R ist, werden Sie natürlich die Funktionen und die Syntax dieser prozeduralen Sprachen verwenden. Mit anderen Worten, trotz der Tatsache, dass die Motivation für SQL darin bestand, standardisierte deklarative Abfragen zu verwenden, sehen Sie in der realen Welt viele datenbankspezifische prozedurale Serverprogrammierungen.
Das führt uns nicht ganz zurück in die schlechten alten Zeiten der CODASYL-Datenbankprogrammierung (obwohl Cursor nahe beieinander liegen), aber es geht von den Ideen zurück, dass SQL-Anweisungen standardisiert werden sollten und dass Leistungsprobleme dem Optimierer für Datenbankabfragen überlassen werden sollten . Am Ende ist eine Verdoppelung der Leistung oft zu viel, um sie auf dem Tisch zu lassen.
Lernen Sie SQL
Die unten aufgeführten Websites können Ihnen beim Erlernen von SQL helfen oder die Macken verschiedener SQL-Dialekte entdecken.