Sql-und-Xml - Home

Sql-Tutorial

Sql - Structured Query Language - Merkmale und Besonderheiten dieser Programmiersprache

Die folgenden Bemerkungen skizzieren einige wesentliche Merkmale von Sql.

Das Verhältnis zwischen Sql und dem Datenbank-Management-System (DBMS)

Im Kapitel Datenbank-Grundbegriffe wurde erläutert, daß das Datenbank-Managementsystem die einzelnen Datenbanken vor dem direkten Zugriff abschirmt und lediglich Schnittstellen anbietet, so daß Nutzer über diese Schnittstellen Daten abfragen und bearbeiten können. Handelt es sich bei dem DBMS um ein relationales DBMS, das Sql unterstützt, dann nimmt diese Schnittstelle Sql-Befehle in Form von Texten / Strings entgegen. Diese werden zunächst von einem passenden Modul auf syntaktische Korrektheit geprüft und von einem Parser in die logischen Einheiten (Schlüsselwörter, Operatoren, Tabellen- und Spaltenbezeichner) zerlegt. Anschließend muß ein Ablaufplan festgelegt werden. Denn für bald jede Sql-Anweisung gibt es verschiedene Möglichkeiten, in welcher Reihenfolge JOIN- und WHERE-Bedingungen auf die beteiligten Tabellen angewandt werden können. Ebenso wird auf dieser Ebene entschieden, ob für Zugriffe Indizes genutzt oder ob ein Tabellenscan vielleicht schneller ist. Der interne Optimierer versucht, aus diesen Varianten jene auszuwählen, welche die geringsten Kosten verursacht, das Ergebnis wird als Ablaufplan im Arbeitsspeicher abgelegt. Anschließend kann dieser ausgeführt und eventuell zurückgegebene Zeilen in eine Art temporäre Tabelle geschrieben werden. Nach Abschluß der Datenoperation liegt eine Statusmeldung vor. Diese wird - eventuell mit den Daten - an die aufrufende Instanz zurückgegeben. Ein Vorteil eines serverbasierten DBMS im Gegensatz zu einem Desktop-System liegt darin, daß ein serverbasiertes DBMS bereits kompilierte Ablaufpläne im Arbeitsspeicher halten und wiederverwenden kann. Bei der nächsten Anforderung mit derselben Befehlsfolge kann der erstellte Ablaufplan wiederverwendet, die für seine Erstellung notwendige Zeit damit eingespart werden. Desktop-basierte Systeme, bei welchen ausschließlich die CPU des Clients diese Operationen ausführt, erstellen in der Regel den Ablaufplan zwar schneller, jedoch auch statischer. Diese Erstellung eines Ablaufplans wird bei jedem Aufruf wiederholt. Bei Zugriffen mehrerer Clients sind voneinander unabhängige CPUs beteiligt, so daß jeder Client seinen eigenen Ablaufplan erstellen muß.

Sql-Ausführung durch einen Interpreter oder eingebettet in eine Wirtssprache

Für das bislang vorgestellte Szenario sind zwei verschiedene Initialisierungen denkbar. Zum einen kann das DBMS selbst oder ein Client eine interaktive Möglichkeit anbieten, welche das direkte Eintippen von Sql-Befehlen erlaubt. Beispiele hierfür sind der 'SQL Query Analyzer' vom MS-SqlServer, das ebenfalls der MSDE-Engine beiliegende OSQL.Exe, falls dieses im interaktiven Modus genutzt wird oder das Sql-interaktiv-lernen aus den Freeware - Tools. Diese Werkzeuge erlauben es, den Sql-Code direkt einzugeben und liefern das Recordset sowie eventuelle Statusmeldungen zurück. Ferner kann ein solches clientseitig verwendetes Tool auch eine graphische Möglichkeit anbieten, Tabellen und Spalten auszuwählen sowie Filterbedingungen zu setzen. Im Hintergrund generiert das Clientsystem aus den Nutzereingaben den zugehörigen Sql-Befehl. Werkzeuge wie der Access-Entwurfsmodus für Abfragen, die Erstellung von Sichten über die graphische Oberfläche innerhalb eines auf der MSDE basierenden Projektes oder eine über ein Webinterface verwaltete Sql-Datenbank zum Mieten sind von diesem Typ. Manche dieser Werkzeuge erlauben ein Umschalten zwischen graphischer Oberfläche und erzeugtem Sql-Code, so daß Sql hierüber unmittelbar zu lernen ist.

Die andere Möglichkeit besteht darin, innerhalb einer Programmiersprache ein Objekt zu verwenden, welches Kontakt zu einem DBMS aufnehmen, diesem Sql-Befehle übergeben und Recordsets sowie Statusmeldungen empfangen und weiterverarbeiten kann. In diesem Fall wird die umgebende Programmiersprache, die Sql-Strings an das DBMS weiterreicht, als Wirtssprache bezeichnet. Der Vorteil einer solchen Architektur besteht darin, daß bei verschiedenen Clients, die womöglich sogar verschiedene Programmiersprachen nutzen, die Ebene des Datenzugriffs getrennt werden kann von der Ebene der clientseitigen Verarbeitung. Den Programmierern des Clients muß nur bekannt sein, welche Daten sie anfordern, dies übergeben sie als Sql-String. Als Ergebnis erhalten sie eine Statusmeldung sowie eine Tabelle, welche sie weiterverarbeiten können. Diese Konzeption läßt sich nochmals entscheidend dadurch verbessern, daß als Befehle nur noch gültige Namen gespeicherter Prozeduren (stored Procedures) zulässig sind, für welche der Client die Ausführungs- (Execute-) Berechtigung besitzt. Damit benötigt die DBMS-Benutzerkennung, über welche sich der Client am Datenbankserver anmeldet, keine der SELECT-, INSERT-, UPDATE- oder DELETE-Berechtigungen, mit welchen er ganze Tabellen bearbeiten könnte. Ebenso muß diese Benutzerkennung nicht Mitglied einer speziellen Nutzergruppe mit besonderen Rechten sein. Schließlich lassen sich hierdurch bei korrekter Initialisierung des Clientobjektes alle Probleme mit Sql Injections vermeiden, mit welchen es ansonsten möglich wäre, den Sql-Code durch die Eingabe zusätzlicher Zeichen in die normalen Datenfelder um eigene Logik zu ergänzen und hierdurch Daten zu manipulieren.

Sql als mengenorientierte Sprache im Gegensatz zum datensatzorientierten Zugriff

Ein für manche Programmierer gewöhnungsbedürftiges Charakteristikum von Sql besteht darin, daß Sql mengenorientiert (set-orientated) und nicht datensatz-orientiert (row-level-orientated) arbeitet. Aus den Daten in mehreren Tabellen wird durch JOIN-Verknüpfungen eine große Tabelle gebildet und diese als Menge abgefragt bzw. bearbeitet. Durch die Festlegung von Spalten und Where-Klauseln - also Zeilen - kann diese Menge in der Breite und in der Höhe eingeschränkt werden. Muß die bislang verwendete Auswahl anhand weitergehender Kriterien verkleinert werden, können Unterabfragen erstellt und diese per JOIN mit der Ausgangstabelle verknüpft werden. Ein datensatzorientiertes Vorgehen würde dagegen eine Tabelle Zeile um Zeile von der Wirtssprache her abrufen, dort auf das Vorliegen gewisser Kriterien prüfen und - falls die Kriterien erfüllt sind - die Daten weiterverarbeiten.

Die eigentlichen Stärken von Sql liegen in der Bearbeitung einer großen Menge 'auf einmal', der Nutzung der speziell optimierten Indizes sowie der Verwendung eines internen Optimierers zur Erstellung eines Ablaufplans und dessen Wiederverwendbarkeit für mehrere Abfragen Diese Stärken kommen nur dann wirklich zum Tragen, wenn datensatzorientierte Befehlsfolgen möglichst selten eingesetzt werden. Alle Probleme der Datenauswahl und der Datenbearbeitung müßten sich ohne die Verwendung zeilenorientierter Operationen bewältigen lassen. Leider finden sich bei Recherchen wiederholt Beispiele, in welchen Datensätze etwa in eine temporäre Tabelle oder in einen Cursor geladen und anschließend Zeile um Zeile verarbeitet werden. Im extremen Fall wird in einer Schleife aus der temporär angelegten Tabelle oder aus dem Cursor eine Datenzeile geholt, diese innerhalb der Wirtssprache auf Eigenschaften geprüft - anstatt diese Kriterien in der WHERE-Bedingung zu formulieren - und weiterverarbeitet. Tatsächlich jedoch dürfte nur in jenen Fällen ein datensatzorientiertes Vorgehen notwendig sein, in welchen aus den selektierten Informationen - etwa Nutzernamen - im Rahmen einer Metaverarbeitung dynamisch Sql-Befehle generiert werden sollen und diese Befehle nur als Einzelanweisungen ausgeführt werden dürfen. Dies gilt beim MS-SqlServer bsp. für Befehle wie Create Procedure ... As ... oder Create View ... As .... Bei diesen speziellen, objekterzeugenden Befehlen muß dieser Create-Befehl am Anfang des Befehlsstapels stehen und darf keinen weiteren Befehl enthalten. Selbst in diesem Fall ist zu prüfen, ob der Befehl nicht bereits in der Sql-Select-Anweisung zusammengesetzt werden kann. Falls ja, genügt es, die Zeile abzurufen und den String sofort auszuführen. Die Alternative, von der Wirtssprache her die Werte abzurufen, erst dort den Sql-Befehl zusammenzusetzen und ihn anschließend auszuführen, ist aufwendiger. Lediglich die aufgrund der Vervielfachung der Texte vergrößerte Tabelle und die hierdurch produzierte größere Speicherauslastung des Clients wäre in wenigen, sehr seltenen Fällen ein Argument, das gegen den Einsatz dieser Technik spricht.

Sql als Sprache der vierten Generation - 4GL

Ein zentrales Merkmal von Sql ist, daß nicht gesagt wird, wie etwas gemacht werden soll, sondern daß nur mitgeteilt wird, was zu tun sei. SQL wird deshalb als eine Sprache der vierten Generation klassifiziert. Programmiersprachen können grob gemäß der folgenden Liste sortiert werden:
  • Sprachen der ersten Generation oder Maschinensprachen: Dies sind Programmiersprachen, die in Binärform, also als Bit-Folge von Nullen und Einsen geschrieben werden. Alternativ können auch Werte aus dem Hexadezimalsystem verwendet werden, so daß ein Block von vier Bit durch die 2^4 = 16 Alternativen 0-9 und A-F belegt werden kann. Ein Byte ist damit durch zwei Werte aus dem Hexadezimalsystem festgelegt.
  • Sprachen der zweiten Generation oder Assemblersprachen: Die Sprachen der ersten Generation sind extrem schlecht lesbar, damit fehleranfällig. Deshalb werden die rohen Maschinenbefehle in wiederkehrenden Gruppen zusammengefaßt und mit englischsprachigen Begriffen benannt. Hinzu kommen Register, die feststehende Namen für Speicheradressen darstellen. Das Ergebnis sind Ausdrücke, die bsp. so aussehen können:
    	mov eax, 100		Wert 100 nach eax schreiben
    	add eax, 10		zum Wert in eax 10 addieren
    Jedem dieser Assembler-Ausdrücke entspricht ein Maschinenbefehl, so daß Assembler-Befehlsfolgen Eins-zu-Eins auf Befehlsfolgen in der Maschinensprache abgebildet werden. Assembler kennt jedoch keine komplexeren Anweisungen, keine Datentypen und kaum die aus höheren Programmiersprachen bekannten Konstrukte der for- und while-Schleifen. Stattdessen müssen bsp. mehrfache Additionen in Einzelanweisungen zerlegt und die übliche if-then-else - Anweisung mittels eines Vergleichs sowie eines anschließenden GoTo - eines Sprungs - behandelt werden. Ferner ist der gesamte Code maschinenabhängig. Damit lassen sich einerseits Programme schreiben, die weitaus schneller sein können als ihr Analogon in der Hochsprache. Andererseits sind die Programme nicht portabel, so daß jede Verteilung von Software begrenzt ist und das Programm für einen neuen Maschinentyp eventuell angepaßt werden muß.
  • Sprachen der dritten Generation oder höhere Programmiersprachen: Diese Gruppe umfaßt Sprachen wie C, Fortran oder VisualBasic, deren Quelltext interaktiv von einem Interpreter direkt interpretiert oder einmalig von einem Compiler in eine ausführbare Datei übersetzt wird. Es stehen komplexe und selbst definierbare Datentypen sowie diverse Schleifenkonstrukte zur Verfügung. Der Quellcode ist in der Regel unabhängig von der Maschine, auf welcher er später ausgeführt wird. Sämtliche maschinenspezifischen Probleme werden vom Compiler behandelt, so daß der Maschinencode aufwendiger ist als ein speziell angepaßter Assemblercode mit demselben Leistungsumfang. Sprachen der dritten Generation sind prozedurorientiert insofern, da das Programm sowohl Datenstrukturen als auch Zugriffsprozeduren selbst definiert und festlegt, wie etwas gemacht werden soll. Damit liegt die Verantwortung bsp. für effiziente Sortier- und Filteralgorithmen oder für die Erstellung eines leistungsfähigen Index beim Programmierer.
  • Sprachen der vierten Generation oder datenorientierte Programmiersprachen: Diese Sprachen sind dadurch gekennzeichnet, daß sie nicht mehr prozess-, sondern datenorientiert sind und daß beim Programmieren nicht mehr Schritt für Schritt festgelegt wird, wie etwas gemacht werden soll, sondern bloß noch bestimmt wird, was zu tun sei. Dies bedeutet einerseits eine Reduktion gegenüber den Sprachen der dritten Generation, da viele Details außerhalb des Einflußbereichs des eigenen Quellcodes liegen und beim Programmieren nur verwendet, jedoch nicht mehr beeinflußt werden können. Andererseits impliziert dies eine Entlastung von diesen wiederkehrenden Routineaufgaben wie dem Entwerfen einer Datenstruktur, dem Speichern und Laden aus der Datei oder der Entwicklung eines effizienten Sortieralgorithmus. Diese Aufgaben bleiben den Entwicklern der Programmierumgebung überlassen, so daß diese bei großen Datenmengen leistungskritischen Aufgaben durch das hierfür notwendige Spezialwissen abgedeckt sind.
  • Sprachen der fünften Generation oder Sprachen der Künstlichen Intelligenz: Im Gegensatz zu den Sprachen von der ersten bis zur vierten Generation, die sich an dem Aufbau von Rechenmaschinen orientieren und imperativ Probleme Befehl um Befehl abarbeiten, verarbeiten die Sprachen der fünften Generation Eingaben deklarativ entlang eines Systems aus Regeln und Schlußfolgerungen.
Die Zuordnung von SQL als Programmiersprache der vierten Generation ergibt sich daraus, daß Sql-Befehle nur noch beschreiben, was gemacht werden soll: 'Erzeuge oder bearbeite ein Objekt, manipuliere diese Daten oder gib jene Daten zurück'. Eine Sprache der dritten Generation müßte ein eigenes Tabellenobjekt definieren und eigenständig Verfahren entwickeln, um bsp. eine Zeile möglichst effizient zu suchen und zurückzugeben. Solche Algorithmen könnten als Objekte mit Methoden gekapselt werden. Damit wäre der Datenzugriff jedoch an diese Programmiersprache gebunden. Wenn man mag, kann man ein relationales DBMS mit einer Sql-Schnittstelle interpretieren als ein allgemeines Objekt, welches mittels geeigneter Connection-Objekte von verschiedenen Programmiersprachen genutzt werden kann. Dieses RDBMS kennt gewissermaßen nur die Methode 'Führe den folgenden Sql-Befehl aus', dessen Eigenschaft (= der auszuführende Sql-Befehl) wird zuvor als Stringvariable übergeben. Die Methode liefert einen Statuswert über Erfolg oder Fehlschlag sowie eventuelle Rückgabedaten aus.

© 2003-2016 Jürgen Auer, Berlin.