von:Manuel Soriano
|
Zusammenfassung:
Dieser kurze Kurs besteht aus drei Teilen. Im ersten Teil erklären
wir einige allgemeine Dinge über SQL, wofür wir einen Public Domain
Datenbankserver namens PostgreSQL verwenden. Im zweiten Teil werden wir
uns näher mit den SQL-Kommandos beschäftigen. Der dritte Teil schließlich
wird uns einige der fortgeschrittenen Möglichkeiten der SQL-Kommandos
zeigen, spezielle Funktionen von PostgreSQL, die für unsere Projekte
hilfreich sein könnten, und schließlich werden wir ein kleines C Programm
betrachten, das dies alles zusammenbringt.
In dieser kurzen Einführung beschränke ich mich auf Datenbanken (DB). Es gibt andere Organisationsformen von Daten, aber hierüber zu schreiben würde den Rahmen dieses Artikels sprengen.
Bis vor kurzem wurde der Zugriff auf Daten über Entities durchgeführt, die über im Datenbankschema wohldefinierte Links miteinander in Beziehung standen. Diese Art des Zugriffs hat Vorteile, besonders bei der Geschwindigkeit des Zugriffs, aber auch einen großen Nachteil: Auf die Daten kann nur über existierende Links zugegriffen werden, z.B.
states -> countries -> countiesaber niemals:
states -> countieswobei » -> « den Link symbolisiert.
Wenn man diese zweite Beziehung etablieren möchten, muß man
das Schema neu definieren und erneut übersetzen...
Das heißt, daß die Beziehungen in einer hierarchischen
DB statisch sind und nur durch Ändern des Schemas und anschließendes
Reorganisieren der Datenbank verändert werden können.
Die Grundidee von relationalen Datenbanken besteht nun darin, genau diese Beziehungen zwischen den Daten während der Abfrage aufzubauen, ohne statische Links zu gebrauchen, sondern statt dessen Bezeichner zu verwenden, die das Verbinden einer Liste mit einer anderen unterstützen.
Nach dieser Einführung braucht mancher evtl. ein Aspirin :)
Relationale Datenbankmanager brauchen keine statischen Links, um ein Absteigen in die Hierarchie der Entities zu ermöglichen, sondern sie verwenden einen eindeutigen Schlüssel, der diese Entities identifiziert, während er als Ergebnis der Abfrage eine temporäre Beziehung etabliert.
Diese Identifikation ist nichts weiter als eine Kodierung . Bsp.: Meine Telefonnummer ist nicht:
1234567
sondern :
34 6 1234567
Meine Telefonnummer ist eindeutig beschrieben durch den Staatenschlüssel(34), den Länderschlüssel (6) und die Gerätenummer (1234567).
Alle Kreise haben einen Schlüssel und gehören zu einem Land und einem Staat.
Alle Länder haben einen Schlüssel und gehören zu einem Staat.
Alle Staaten haben einen Schlussel.
Um alle Kreise eines Landes zu finden, stellen wir eine Beziehung zwischen
den Kreisen und dem Land über den Landes- und den Staatenschlüssel
her; um alle Kreise eines Staates zu finden, stellen wir eine Beziehung
zwischen dem Kreis und dem Staat über den Staatenschlüssel her.
Diese Beziehungen sind temporär und existieren nur für die Dauer
der Abfrage.
Dies ist alles ein wenig trocken und schwer zu verstehen, aber ich hoffe,
mit den ersten paar Beispielen das Konzept der Kodierung und dem, was dazugehört,
klarer machen zu können.
Als ich die erste Anfrage an den Datenbankmanager sendete, lieferte
mir dieser alle zugehörigen Einträge. Aber welche Daten habe
ich wirklich empfangen? Die Vereinigungsmenge der Staats- und Kreis-Einträge,
für jeden Kreiseintrag erhielt ich die damit verbundenen Staaten-Einträge.
Während meiner ersten Anfrage ist eine neue, namenlose Entity entstanden,
die eine Kopie der Staaten und Kreise enthielt. Diese neue Instanz
verschwand nach meiner Anfrage wieder.
Bisher sind wir es gewohnt, eine Menge von Daten eine Datei zu nennen.
Diese bestehen aus Datensätzen, und jeder Satz hat Felder. In relationalen
Datenbanken wird eine solche Datei »Tabelle« genannt, eine
Tabelle besteht aus Zeilen, und jede Zeile enthält Spalten, also nur
eine kleine kosmetische Korrektur. ;-)
Nebenbei bemerkt führen einige wenige hierarchische Datenbankmanager SQL als Abfragesprache ein. (Anm. d. Übers.: SQL bedeutet Standard Query Language.) Aber dies ist nur eine Anektdote am Rande. Die Sprache SQL wird nahezu ausschließlich bei relationalen Datenbanksystemen verwendet.
Um die Verwendung von SQL zu illustrieren, verwenden wir den relationalen Datenbankmanager PostgreSQL. Obwohl er den Regeln von SQL nicht vollständig entspricht, kommt er ihnen für unsere Zwecke doch ausreichend nah, und er ist ein ausgezeichneter Manager für für den "Hochleistungseinsatz" .
Ich werde den Installationsprozeß nur kurz beschreiben, denn das
eigentliche Thema dieses Artikels ist SQL. Zunächst laden wir die
Quelltexte von www.postgresql.org
, wo auch einige Patch-Dateien verfügbar sind. Dann entpacken wir
die Quelldateien (mit »tar xvzf <dateiname>«) relativ zum
lokalen Verzeichnis, und wechseln (mit »cd postgresql-6.3«)
in das entstandene Unterverzeichnis.
cd src ./configure --prefix=/the/desired/path make all >& make.log & tail -f make.log export PATH=$PATH:/the/desired/path/pgsql/bin export MANPATH=$MANPATH:/the/desired/path/pgsql/man export PGLIB=/the/desired/path/pgsql/lib export PGDATA=/the/desired/path/pgsql/data initdb createdb test psql test Welcome to the POSTGRESQL interactive sql monitor: Please read the file COPYRIGHT for copyright terms of POSTGRESQL type \? for help on slash commands type \q to quit type \g or terminate with semicolon to execute query You are currently connected to the database: postgres test=>Dies ist die Befehlsaufforderung (Prompt) von PostgreSQL, nun können wir mit der Eingabe von Befehlen beginnen.
mytest=>create table mytest (field1 varchar(10)); CREATE mytest=>insert into mytest values ('hello'); INSERT number 1 mytest=>commit work; NOTICE:EndTransactionBlock and not inprogress/abort state END mytest=>select * from mytest; field1 ------ hello (1 row) mytest=>drop table mytest; DROP mytest=>Ctrl-dNun haben wir die SQL-Konsole schon wieder verlassen.
Wer Probleme mit dem Übersetzen und Installieren von PostgreSQL hat, der schaue bitte in die Datei »INSTALL« im Startverzeichnis der Distribution.
Lassen Sie mich eine weitere Randbemerkung machen; ein Datenbankserver
besteht generell aus folgenden Teilen:
Datei: countries.sql create table countries (cod_country integer, name varchar(30)); insert into countries values (1, 'country 1'); insert into countries values (2, 'country 2'); insert into countries values (3, 'country 3'); commit work;
Datei: states.sql create table states (cod_State int, cod_country int, nam_State varchar(30)); insert into states values (1, 1, 'State 1, Country 1'); insert into states values (2, 1, 'State 2, Country 1'); insert into states values (1, 2, 'State 1, Country 2'); insert into states values (2, 2, 'State 2, Country 2'); insert into states values (1, 3, 'State 1, Country 3'); insert into states values (2, 3, 'State 2, Country 3'); commit work;
Datei: counties.sql create table counties (cod_country int, cod_state int, cod_county int, nam_county varchar(60)); insert into counties values (1, 1, 1, 'County 1, State 1, Country 1'); insert into counties values (2, 1, 1, 'County 2, State 1, Country 1'); insert into counties values (3, 1, 1, 'County 3, State 1, Country 1'); insert into counties values (1, 2, 1, 'County 1, State 2, Country 1'); insert into counties values (2, 2, 1, 'County 2, State 2, Country 1'); insert into counties values (3, 2, 1, 'County 3, State 2, Country 1'); insert into counties values (1, 3, 1, 'County 1, State 3, Country 1'); insert into counties values (2, 3, 1, 'County 2, State 3, Country 1'); insert into counties values (3, 3, 1, 'County 3, State 3, Country 1'); insert into counties values (1, 1, 2, 'County 1, State 1, Country 2'); insert into counties values (2, 1, 2, 'County 2, State 1, Country 2'); insert into counties values (3, 1, 2, 'County 3, State 1, Country 2'); insert into counties values (1, 2, 2, 'County 1, State 2, Country 2'); insert into counties values (2, 2, 2, 'County 2, State 2, Country 2'); insert into counties values (3, 2, 2, 'County 3, State 2, Country 2'); insert into counties values (1, 3, 2, 'County 1, State 3, Country 2'); insert into counties values (2, 3, 2, 'County 2, State 3, Country 2'); insert into counties values (3, 3, 2, 'County 3, State 3, Country 2'); insert into counties values (1, 1, 3, 'County 1, State 1, Country 3'); insert into counties values (2, 1, 3, 'County 2, State 1, Country 3'); insert into counties values (3, 1, 3, 'County 3, State 1, Country 3'); insert into counties values (1, 2, 3, 'County 1, State 2, Country 3'); insert into counties values (2, 2, 3, 'County 2, State 2, Country 3'); insert into counties values (3, 2, 3, 'County 3, State 2, Country 3'); insert into counties values (1, 3, 3, 'County 1, State 3, Country 3'); insert into counties values (2, 3, 3, 'County 2, State 3, Country 3'); insert into counties values (3, 3, 3, 'County 3, State 3, Country 3'); commit work;Eine Datei mit SQL-Kommandos kann wie folgt von pgsql aus ausgeführt werden:
\i file_name
Die Kommandos können aber genausogut mittels cut & paste eingefügt werden.
Als nächstes betrachten wir, welche Landkreise verfügbar sind:
manu=> select * from counties; cod_country|cod_state|cod_county|nam_county -----------+---------+----------+---------------------------- 1| 1| 1|County 1, State 1, Country 1 2| 1| 1|County 2, State 1, Country 1 3| 1| 1|County 3, State 1, Country 1 1| 2| 1|County 1, State 2, Country 1 2| 2| 1|County 2, State 2, Country 1 3| 2| 1|County 3, State 2, Country 1 1| 3| 1|County 1, State 3, Country 1 2| 3| 1|County 2, State 3, Country 1 3| 3| 1|County 3, State 3, Country 1 1| 1| 2|County 1, State 1, Country 2 2| 1| 2|County 2, State 1, Country 2 3| 1| 2|County 3, State 1, Country 2 1| 2| 2|County 1, State 2, Country 2 2| 2| 2|County 2, State 2, Country 2 3| 2| 2|County 3, State 2, Country 2 1| 3| 2|County 1, State 3, Country 2 2| 3| 2|County 2, State 3, Country 2 3| 3| 2|County 3, State 3, Country 2 1| 1| 3|County 1, State 1, Country 3 2| 1| 3|County 2, State 1, Country 3 3| 1| 3|County 3, State 1, Country 3 1| 2| 3|County 1, State 2, Country 3 2| 2| 3|County 2, State 2, Country 3 3| 2| 3|County 3, State 2, Country 3 1| 3| 3|County 1, State 3, Country 3 2| 3| 3|County 2, State 3, Country 3 3| 3| 3|County 3, State 3, Country 3 (27 rows) manu=>Als Ergebnis erhalten wir 27 Zeilen, und pgsql wartet nun auf das nächste Kommando. Wir probieren folgendes:
manu=> select * from countries, states; cod_country|name |cod_state|cod_country|nam_state -----------+---------+---------+-----------+------------------ 1|country 1| 1| 1|State 1, Country 1 2|country 2| 1| 1|State 1, Country 1 3|country 3| 1| 1|State 1, Country 1 1|country 1| 2| 1|State 2, Country 1 2|country 2| 2| 1|State 2, Country 1 3|country 3| 2| 1|State 2, Country 1 1|country 1| 1| 2|State 1, Country 2 2|country 2| 1| 2|State 1, Country 2 3|country 3| 1| 2|State 1, Country 2 1|country 1| 2| 2|State 2, Country 2 2|country 2| 2| 2|State 2, Country 2 3|country 3| 2| 2|State 2, Country 2 1|country 1| 1| 3|State 1, Country 3 2|country 2| 1| 3|State 1, Country 3 3|country 3| 1| 3|State 1, Country 3 1|country 1| 2| 3|State 2, Country 3 2|country 2| 2| 3|State 2, Country 3 3|country 3| 2| 3|State 2, Country 3 (18 rows)18 Zeilen??? Wir haben drei Staaten und sechs Länder eingefügt, jedes verweist auf einen einzelnen Staat. Wie ist es möglich, daß wir 18 Zeilen bekommen?
Der letzte Befehl hat eine Vereinigung von zwei Tabellen erzeugt, wir haben die Tabelle der Staaten mit der der Kreise in Beziehung gesetzt, und da wir keine Vereinigungsregel definiert haben, hat pgsql ALLE möglichen Zeilen der Staaten mit ALLEN Zeilen der Länder in Beziehung gesetzt, also hier drei Zeilen für Staaten mal sechs Zeilen mit Ländern, also insgesamt 18. Das Resultat ist völlig unlogisch und nutzlos, deshalb versuchen wir das folgende:
manu=> select * from countries, states manu-> where countries.cod_country = states.cod_country; cod_country|name |cod_state|cod_country|nam_state -----------+---------+---------+-----------+------------------ 1|country 1| 1| 1|State 1, Country 1 1|country 1| 2| 1|State 2, Country 1 2|country 2| 1| 2|State 1, Country 2 2|country 2| 2| 2|State 2, Country 2 3|country 3| 1| 3|State 1, Country 3 3|country 3| 2| 3|State 2, Country 3 (6 rows)Dies scheint ein wenig sinnvoller zu sein. Sind diese sechs Zeilen richtig?
Wir haben sechs Kreise, und jeder Kreis ist in einem Staat. Es ist sinnvoll, eine Anzahl von Zeilen zu erhalten, die der der Kreise entspricht, denn die Staaten sind ein Merkmal der Kreise. Wir setzten einfach die Tabelle der Staaten mit der Tabelle der Länder unter Zuhilfenahme des Staatenschlüssels in Beziehung. Erinnern wir uns daran, daß jeder Staat einen eindeutigen Schlüssel besitzt, und die Länder den zugehörigen Staatenschlüssel enthalten.
Warum countries.cod_country = states.cod_country ?
Der Staatenschlüssel ist sowohl in der Tabelle der Staaten als auch in der Landkreistabelle jeweils cod_country, also ist
cod_country = cod_countryunlogisch, der Interpreter kann nicht unterscheiden, welche der beiden gemeint ist und liefert eine Fehlermeldung:
select * from countries, states where cod_country = cod_country; ERROR: Column cod_country is ambiguousAls nächstes verwenden wir einen Alias für jeden Tabellennamen:
manu=> select * from countries a, states b manu-> where a.cod_country = b.cod_country; cod_country|name |cod_state|cod_country|nam_state -----------+---------+---------+-----------+------------------ 1|country 1| 1| 1|State 1, Country 1 1|country 1| 2| 1|State 2, Country 1 2|country 2| 1| 2|State 1, Country 2 2|country 2| 2| 2|State 2, Country 2 3|country 3| 1| 3|State 1, Country 3 3|country 3| 2| 3|State 2, Country 3 (6 rows)Was gibt der Manager zurück?
manu=> select a.cod_country, cod_state, name, nam_state manu-> from countries a, states b manu-> where a.cod_country = b.cod_country; cod_country|cod_state|name |nam_state -----------+---------+---------+------------------ 1| 1|country 1|State 1, Country 1 1| 2|country 1|State 2, Country 1 2| 1|country 2|State 1, Country 2 2| 2|country 2|State 2, Country 2 3| 1|country 3|State 1, Country 3 3| 2|country 3|State 2, Country 3 (6 rows)Mit dem letzten Kommando fragen wir ausdrücklich nach dem Staatenschlüssel, dem Landesschlüssel und dem Namen von Staat und Land. Beachtenswert ist, daß einige Spaltennamen qualifiziert angegeben werden (a.cod_country), während andere unqualifiziert angegeben werden können (nam_state). Dies kommt daher, daß cod_country in beiden Tabellen vorkommt, während nam_state nur in countries existiert. Eindeutige Spaltennamen müssen nicht ausdrücklich qualifiziert werden.
Nun machen wir die Dinge noch ein wenig komplizierter:
manu=> select a.cod_country, cod_state, name, nam_state manu-> from countries a, states b manu-> where a.cod_country = b.cod_country manu-> and a.cod_country = 3; cod_country|cod_state|name |nam_state -----------+---------+---------+------------------ 3| 1|country 3|State 1, Country 3 3| 2|country 3|State 2, Country 3 (2 rows)Dieses Mal beschränken wir die Suche auf die Landesziffer 3.
select count(*) from states; count ----- 27 (1 row)Wir erhalten die Anzahl der Zeilen, die in der Tabelle der Staaten enthalten sind.
manu=> select cod_country, count(*) from states manu-> group by cod_country; cod_country|count -----------+----- 1| 2 2| 2 3| 2 (3 rows)Dieses Beispiel liefert die Anzahl der Zeilen mit identischem Länderschlüssel, dies ist der Grund für die Verwendung von cod_country.
Ein etwas besseres Beispiel:
manu=> select name, count(*) from countries a, states b manu-> where a.cod_country = b.cod_country manu-> group by name; name |count ---------+----- country 1| 2 country 2| 2 country 3| 2 (3 rows)Wir erhalten immer noch die selben drei Zeilen, aber diesmal sind die zurückgelieferten Informationen verständlicher.
So, bisher haben wir nur eine Einführung gegeben, quasi zum Aufwärmen
:-)
Wir haben verschiedene Kommandos benutzt:
CREATE TABLE | Dieses Kommando erzeugt eine Tabelle mit ihren Zeilen. |
DROP TABLE | Löscht eine Tabelle. |
SELECT | Dieses Kommando ist die Grundlage von SQL, es erlaubt das Erzeugen
von temporären Tabellen, die nur die benötigten Informationen
enthalten. SELECT kann als Argumente sowohl Funktionen als auch komplexe
Ausdrücke oder sub_selects enthalten:
select count(*) from states where cod_country in (select cod_country from countries); count ----- 27 (1 row) |
COMMIT WORK | Dies ist ein weiteres grundlegendes Kommando. Es teilt dem DB-Manager
mit, daß er alle seit BEGIN WORK gemachten Veränderungen akzeptieren
soll. Für unseren speziellen DB-Manager ist BEGIN WORK der Startpunkt
einer Transaktion, bei anderen Managern beginnt eine Transaktion mit dem
ersten Befehl, der etwas in der Datenbank verändert. In PostgreSQL
wirken alle Befehle, die die Daten verändern, direkt, es sei denn,
es existiert ein führendes BEGIN WORK.
Hinweis: Befehle, die das Datenbankschema verändern, führen ein COMMIT WORK aus; dadurch werden geöffnete Transaktionen sofort geschlossen, und es ist nicht mehr möglich, ein ROLLBACK WORK durchzuführen. Solange ein Benutzer eine Transaktion geöffnet hält, kann er bestimmen, wie andere Benutzer auf seine Daten zugreifen können, festlegen.
|
ROLLBACK WORK | Beendet eine Transaktion und erklärt die darin gemachten Änderungen für ungültig. Der Befehl ROLLBACK WORK verwirft die in der Transaktion gemachten Änderungen und bringt die Daten auf den Stand vor Beginn der Transaktion zurück. |
Das Konzept der Transaktion ist sehr wichtig, sie ermöglicht bei Auftreten eines Fehlers das Zurückgehen auf den vorherigen Zustand. Wir testen diese Operation und führen zuerst eine »rollback work«-Aktion durch, um eine vorherigen Transaktionen zu stornieren:
manu=> select * from countries; cod_country|name -----------+--------- 1|country 1 2|country 2 3|country 3 (3 rows)
Wir erhalten drei Zeilen
begin work;Startet die Transaktion
insert into countries values (5, 'Country Not True');Wir fügen eine Zeile ein. Als nächstes überprüfen wir, daß alle Zeilen vorhanden sind
manu=> select * from countries; cod_country|name -----------+---------------- 1|country 1 2|country 2 3|country 3 5|Country Not True (4 rows)Alle Zeilen sind da. Als nächstes schreiben wir:
rollback work;Dies bricht die Transaktion ab.
manu=> select * from countries; cod_country|name -----------+--------- 1|country 1 2|country 2 3|country 3 (3 rows)Wir untersuchen die Tabelle erneut und stellen fest, daß sie nun wieder die ursprünglichen drei Zeilen enthält.
INSERT | Dieses Kommando kennen wir bereits. Es fügt Daten in eine Tabele ein. | ||||||||||||||||||||
CREATE TABLE | Ein weiteres wichtiges Kommando, es erzeugt eine Tabelle und ihre Spalten;
betrachten wir die möglichen Datentypen:
|
Die Definition dieser Datentypen ist für jeden SQL-Manager unterschiedlich,
obwohl es einen SQL-Standard gibt (ANSI/92
oder SQL/3), der ein paar Typen mit ihren Eigenschaften definiert. In diesem
Kurs werden wir nur einige spezielle Typen von PostgreSQL verwenden.
DELETE | Löscht Zeilen einer Tabelle. |
UPDATE | Modifiziert Spalten einer Tabellenzeile. |
SQL erlaubt es, eine Abstraktionsebene über die Daten zu legen und unterstützt uns dabei, diese so zu verwalten, wie wir es brauchen.
Nun kann man sich fragen, wie man SQL in einer Anwendung verwenden kann.
Die Antwort wird Schritt für Schritt kommen; in unserem dritten Artikel werden wir eine kleine C-Anwendung besprechen, die SQL verwendet.
Webpages maintained
by Miguel Ángel Sepúlveda
© Manuel Soriano 1998 Übersetzt von:Kai Debus LinuxFocus 1998 |