Datenbank mit C - Teil 1



  • Inhaltsverzeichnis

    1 Vorbemerkung
    2 MySQL installieren und starten

    2.1 Linux
    2.2 Windows
    2.3 Client starten

    3 Kurze Einführung in SQL
    4 MySQL C-API

    1 Vorbemerkung

    Dieser Artikel handelt von der Datenbank-Programmierung in der Programmiersprache C.
    Ziel dieses Artikels ist es, eine Datenbankverbindung zu MySQL herzustellen und die
    SQL-Befehle auf die Datenbank anzuwenden. Es geht in erster Linie also nicht um SQL selbst.
    Hier wird nur eine kurze Einführung gegeben. Wer sich mehr mit dieser Datenbank-Abfragesprache
    beschäftigen will, sei auf die Dokumentation von der MySQL-Webpage verwiesen.
    Die Beispiele sollten bei den MySQL-Versionen 4.0, 4.1 und 5.0 funktionieren.

    2 MySQL installieren und starten

    2.1 GNU/Linux

    Da MySQL Bestandteil von jeder GNU/Linux-Distribution ist, sollte es mit der Installation
    auch kaum Probleme geben. Bitte folgende Pakete installieren:

    • mysql
    • mysql-client
    • mysql-devel - die Include-Dateien
    • mysql-bench
    • mysql-shared

    Sie benötigen root-Rechte, um den Server zu starten. Unter Red Hat starten Sie den Server mit

    # /etc/rc.d/init.d/mysql start
    

    sollte auch mit SuSE funktionieren, dort geht aber auch

    # rcmysql start.
    

    oder wie bei Debian

    # /etc/init.d/mysql start
    

    Kommt darauf an, welche Distribution und/oder Versionsnummer vorliegt.

    2.2 Windows

    Im Gegensatz zu GNU/Linux werden Sie wohl hier die MySQL-Binaries herunterladen müssen. Gehen Sie zu der
    MySQL-Webpage und laden Sie dann die Binary-Version herunter.
    Ich habe mir die Windows Essential(x86)-Version 4.1.22 heruntergeladen.

    Jetzt haben Sie eine msi-Datei auf Ihrem Rechner. Um MySQL zu installieren, müssen Sie sich als Administrator anmelden.
    Die Datei aufrufen.

    Hier Custom wählen.

    Mit der linken Maustaste auf das Icon bei "C Include Files/Lib Files klicken" und beim Auswahlmenü "alles installieren mit Unterverzeichnisse" anwählen.
    Für diejenigen, die den Lcc-win32-Compiler benutzen, bitte das Installationsverzeichnis in einem Wort angeben, also: C:\Programme\MySQL\MySQLServer41.
    Könnte sonst evtl. Probleme geben.

    Diese Einstellung wählen. Im nächsten Fenster "Standard Configuration" wählen.

    Mit diesen Einstellungen wird MySQL als Systemdienst gestartet und in den Suchpfad eingetragen.

    Sie können am Ende der Installation für root auch ein Passwort vergeben, müssen es aber nicht.
    Nach der Installation die libmySQL.dll in ein Systemverzeichnis kopieren, am besten nach C:\Windows\system32

    Danach starten Sie den Server unter Windows NT und 2000 mit

    NET USE MySQL
    

    .
    Stoppen mit

    NET STOP MySQL
    

    Um MySQL als Systemdienst unter Windows NT oder 2000 zu installieren (falls noch nicht geschehen):

    C:\Programme\MySQL\MySQL Server 4.1\bin\mysqld-nt --install
    

    oder

    C:\Programme\MySQL\MySQL Server 4.1\bin\mysqld-max-nt --install
    

    Starten mit Windows 98

    C:\Programme\MySQL\MySQL Server 4.1\bin\mysqld
    

    stoppen

    C:\Programme\MySQL\MySQL Server 4.1\bin\mysqladmin -u root -shutdown
    

    Falls bei Ihnen kein mysql-Verzeichnis unter C: angelegt wurde, kann der Pfad auch wie oben bei der Installation gezeigt

    C:\Programme\MySQL\MySQL Server <Versionsnummer>
    

    sein. Manchmal wird MySQL auch standardmäßig nach C:\mysql installiert.

    2.3 Client starten

    Öffnen Sie eine Kommandozeile bzw. ein Terminal und geben Sie folgendes ein:

    # mysql -u root -h localhost
    

    Jetzt sollten Sie mit

    mysql>
    

    den Eingabecursor vor sich haben. Geben Sie einfach mal

    USE mysql;
    

    ein, danach

    SELECT user, host, password FROM user;
    

    und schauen sich das Ergebnis an. Das war nur ein erster Test. Alles weitere
    über Abfragen usw. im nächsten Abschnitt. Mit \q beenden Sie den Client wieder.

    3 Kleine Einführung in SQL

    MySQL ist eine relationale Datenbank. Dieser Begriff wurde von Edgar F. Codd in den 1970er Jahren geprägt.
    1985 hat Codd 12 Grundregeln aufgestellt, die ein relationales Datenbanksystem erfüllen muss. Das Datenmodell
    basiert auf den mathematischen Grundlagen der relationalen Algebra und bietet dem Anwender ein Höchstmaß
    an Flexibilität, im Vergleich zu anderen Methoden der Datenspeicherung.
    z.B.

    Alle Daten (Anwender- wie auch Systemdaten) in einer relationalen Datenbank werden logisch als
    Zeilen in einer Tabelle dargestellt. Somit besteht aus der Benutzersicht jede relationale
    Datenbank nur aus Tabellen.

    Eine Zeile in einer Datenbanktabelle wird als Datensatz bezeichnet.
    Eine Spalte als Attribut, z.B. Vorname.

    Die Daten in den Tabellen stehen in bestimmten Beziehungen zu einander. Ein Schulungsunternehmen hat in
    verschiedenen Orten Schulen zur Weiterbildung. Es besteht je eine Tabelle für Schulen, Dozenten,
    Teilnehmer und Kurse.

    1:1-Beziehung: eine Schule hat einen Dozenten als Leiter.
    1:n-Beziehung: eine Schule hat mehrere Dozenten, die dort lehren.
    n:m-Beziehung: mehrere Teilnehmer nehmen an mehreren Kursen Teil, oder
    mehrere Dozenten unterrichten mehrere Kurse.

    Structured Query Language (SQL) ist eine speziell für relationale Datenbanken entwickelte nonprozedurale
    Abfragesprache, deren Anweisungen einer standardisierten Syntax entsprechend als Zeichenkette ausgedrückt
    werden kann. SQL-Anweisungen können auch in prozedurale Programmiersprachen - z.B. C - eingebettet werden
    (Embedded SQL).

    Die SQL-Sprachdefinition umfasst drei Anweisungsgruppen:

    Datendefinition (Data Definition Language)

    Anweisungen zur Verwaltung von Datenbankstrukturen.
    Beispiel: Erstellen oder ändern einer Tabelle

    Datenmanipulation (Data Manipulation Language)

    Anweisungen zur Bearbeitung der Daten, die vom
    Datenbanksystem verwaltet werden.
    Beispiel: Neuaufnahme oder Suchen eines Datensatzes

    Datenkontrolle (Data Control Language)

    Anweisungen zur Vergabe bzw. zum Entzug von Zugriffsrechten und zur
    Transaktionssicherung
    Beispiel: Vergabe des Rechts, Daten in einer Tabelle zu ändern.

    Auch in MySQL gibt es Datentypen, ähnlich einer Programmiersprache. Hier eine Übersicht aus dem Referenzmanual
    von MySQL. Wenn Sie ZEROFILL für eine Spalte angeben, fügt MySQL automatisch der Spalte ein UNSIGNED-Attribut zu.
    Folgende Codebuchstaben werden in der Beschreibung benutzt:
    * M
    Gibt die maximale Anzeigebreite an. Die größte erlaubte Anzagebreite ist 255.
    * D
    Trifft auf Fließkomma-Typen zu und bezeichnet die Anzahl von Ziffern nach dem
    Dezimalpunkt. Der größte mögliche Wert ist 30, sollte aber nicht größer sein als
    M-2.

    Ganzzahlen (Integer)

    TINYINT[(M)] 1 Byte
    -128 bis 127, UNSIGNED 0 bis 255

    SMALLINT[(M)] 2 Byte
    -32768 bis 32767, UNSIGNED 0 bis 65536

    MEDIUMINT[(M)] 3 Byte
    -83886008 bis 83886007, UNSIGNED 0 bis 16777215

    INT(M), INTEGER[(M)] 4 Byte
    -2147483648 bis 2147483647, UNSIGNED 0 bis 4294967296

    BIGINT[(M)] 8 Byte
    -9223372036854775808 bis 9223372036854775807
    UNSIGNED 0 bis 18446744073709551616

    Gleitpunktzahlen

    FLOAT[(M,D)] 4 Byte
    -3.402823466E+38 bis 1.175494351E-38 und
    1.175494351E-38 bis 3.402823466E+38

    DOUBLE[(M,D)] 8 Byte
    -1.7976931348623157E+308 bis -2.2250738585072014E-308 und
    2.2250738585072014E-308 bis 1.7976931348623157E+308

    DECIMAL[(M[,D])] M Byte(M*8 Bit) Abhägig von M und D, z.B. DECIMAL(8,2)

    Zeichen und Strings

    CHAR(M) M Anzahl Zeichen, Zeichenkette fester Länge 1 bis max 255 Zeichen

    VARCHAR(M) M+1 Anzahl Zeichen, Zeichenkette variabler Länge 1 bis max 255 Zeichen

    TINYTEXT, TINYBLOB Zeichenzahl+1 Byte, Zeichenkette variabler Länge bis max 255 Zeichen

    TEXT, BLOB Zeichenzahl + 2 Byte, Zeichenkette variabler Länge bis max 65536 Zeichen

    MEDIUMTEXT, MEDIUMBLOB Zeichenzahl+3 Byte, Zeichenkette mit variabler Länge bis max 2h24 Zeeichen

    LONGTEXT, LONGBLOB Zeichewnzahl+4 Byte, Zeichenkette mit variabler Länge bis max 2h32 Zeichen

    Datum und Zeit

    DATE 3 Byte, Datum von 1000-01-01 bis 9999-12-31, Format YYYY-MM-DD

    DATETIME 8 Byte, Datum und Zeit
    von 1000-01-01 00:00:00 bis 9999-12-31 23:59:59, Format YYYY-MM-DD HH:MM:SS

    TIMESTAMP[(M)] 4 Byte, UNIX-Zeitstempel von Datum und Zeit
    von 1970-01-01 00:00:00 bis 2036-12-31 23:59:59
    Format YYYY-MM-DD HH:MM:SS Format M = 6 YYMMDD
    M = 8 YYYYMMDD
    M = 12 YYMMDDHHMMSS
    M = 14 YYYYMMDDHHMMSS

    TIME 3 Byte, Zeit von -838:59:89 bis 838:59:59 Format HH:MM:SS

    YEAR[(2|4)] 1 Byte
    Jahr in 2- oder 4-Ziffernformat (Vorgabe ist 4-Ziffern)
    Die zulässigen Werte reichen von 1901 bis 2155 sowie 0000
    im 4-Ziffern-Jahresformat und von 1970 bis 2069 beim
    2-Ziffernformat (70 bis 69).MySQL zeigt YEAR-Werte im YYYY-Format an,
    gestattet aber YEAR-Spalten Werte als Zeichenketten oder als Zahlen zuzuweisen

    Starten Sie MySQL wie oben beschrieben - Server und Client. Bei einer Neuinstallation ist das root-Passwort nicht gesetzt.
    Die Vergabe eines Passwortes bei GNU/Linux erfolgt über das Programm mysqladmin auf der Konsole.

    # mysqladmin -u root -h localhost password 'geheim'
    

    Passwort geheim für den user root. Dann anmelden mit

    mysql -u root -h localhost -p
    

    Jetzt werden Sie aufgefordert das Passwort einzugeben.

    Die Schreibweise der Befehle usw. in der Eingabeaufforderung von MySQL ist eigentlich egal, außer bei GNU/Linux, wo Sie sich an die
    exakte Schreibweise für schon angelegte Datenbanken halten müssen. Standardmäßig legt MySQL zwei Datenbanken an: mysql und test.
    mysql ist die System-Datenbank, dort werden User, Hosts, Passwörter und Zugriffsrechte verwaltet. Die Datenbank test ist leer.
    Eine Befehlszeile wird immer mit einem Semikolon abgeschlossen. Mit

    SHOW DATABASES;
    

    können Sie sich alle vorhandenen Datenbanken anzeigen lassen.

    CREATE DATABASE [IF NOT EXISTS] <Name_der_Datenbank>;
    legen Sie eine neue Datenbank an. IF NOT EXISTS ist optional. Falls die Datenbank schon existiert wird keine Fehlermeldung ausgeben
    und sie wird natürlich auch nicht nochmal angelegt:

    CREATE DATABASE IF NOT EXISTS weiterbildung;
    

    Um mit der Datenbank weiterbildung zu arbeiten, müssen Sie zu ihr wechseln, das wird mit

    USE weiterbildung;
    

    erledigt.

    DROP DATABASE [IF EXISTS] <Name_der_Datenbank>;
    Löscht eine Datenbank, falls sie existiert:

    DROP DATABASE IF EXISTS weiterbildung;
    

    Wenn Sie eine neue Datenbank angelegt haben, ist natürlich noch keine Tabelle vorhanden. Mit dem folgenden Befehl erzeugen Sie eine
    Tabelle mit ihren Attributen:

    CREATE TABLE dozent (
    	d_nr SMALLINT UNSIGNED NOT NULL,
    	d_vorname VARCHAR(20),
    	d_nachname VARCHAR(20),
    	d_gesch VARCHAR(1),
    	d_gebdat DATE,
    	d_besch SMALLINT UNSIGNED);
    

    Mit d_nr haben wir einen Index als vorzeichenlosen Datentyp von SMALLINT definiert. NOT NULL besagt, dass er in jedem Fall
    gefüllt werden muss.

    Die Tabelle wird folgendermaßen mit Werten gefüllt:

    INSERT INTO dozent(d_nr, d_vorname, d_nachname, d_geschl, d_gebdat, d_besch) VALUES (1, 'Anna', 'Seger', 'w', '1955-04-13', 1);
    

    Wichtig hierbei ist, dass unten genauso viele Werte im gleichen Format stehen, wie oben Spalten angegeben sind. Strings, die zu lang sind, werden abgeschnitten.

    Mit

    EXPLAIN dozent;
    

    oder

    DESC dozent;
    

    können Sie die Tabellendefinition überprüfen.

    Tabellen löschen Sie mit:
    DROP TABLE [IF EXISTS] <tabelle_name>;

    DROP TABLE IF EXISTS dozent;
    

    Tabellendefinition ändern
    Nichts im Leben ist so perfekt, als dass man es im Nachhinein nicht noch verbessern könnte ;). Das trifft natürlich
    auch auf Datenbanken zu. Weshalb es auch eine Reihe von Befehlen gibt, mit denen Sie im Nachhinein vorhandene Objekte
    verändern können. Allerdings ist auch das, wie das Anlegen von Tabellen, eine komplexe Angelegenheit. Der Befehl zum
    Bearbeiten der Tabellenstruktur lautet ALTER TABLE, dem der Name der zu verändernden Tabelle folgt. Anschließend
    folgen die Schlüsselwörter ADD, MODIFY oder DROP, die zu dieser Tabelle ein neues Element hinzufügen,
    eine bestehende Spalte ändern oder ein bereits existierendes Element löschen.

    ALTER TABLE <name_der_tabelle>
    	{ add { <spalten_name>
    		<constraint_definition> }
    	  modify <name_der_spalte> <neue_definition> |
    	  drop { column | constraint } <name_des_elements> }
    

    Beispiel:

    ALTER TABLE dozent ADD d_firma VARCHAR(20);
    

    Legt am Ende der Tabelle dozent eine Spalte d_firma an.

    ALTER TABLE dozent ADD d_str VARCHAR(20) AFTER d_nr;
    

    Legt eine Spalte d_str nach d_nr an.

    ALTER TABLE dozent DROP d_str;
    

    löscht die Spalte d_str wieder.

    ALTER TABLE dozent DROP d_firma;
    

    löscht die Spalte d_firma wieder.

    ALTER TABLE dozent MODIFY d_gebdat VARCHAR(8);
    

    ändert den Typ der Spalte von DATE in VARCHAR(8).

    ALTER TABLE dozent MODIFY d_gebdat DATE;
    

    und wieder zurück.
    Solange eine Tabelle leer ist, kann man eigentlich jede Spalte in jeden beliebigen Datentyp umwandeln. Ansonsten muss
    der neue Datentyp natürlich zum alten passen, bzw. die Konvertierung muss im Datenbankstandard enthalten sein, damit
    hinterher auch etwas vernünftiges herauskommt.
    Die Verlängerung eines Datentyps (z.B. von VARCHAR(10) nach VARCHAR(20)) ist in der Regel immer problemlos möglich.
    Das gilt auch, wenn ein Datentyp in einen höherwertigen konvertiert wird (z.B. SMALLINT nach INT oder VARCHAR(200)
    nach TEXT).
    Verkürzungen eines Datenfeldes ist, sofern überhaupt zulässig, immer mit einem Risiko verbunden. Hierbei können
    Daten verloren gehen. Das Verkürzen ist meistens nur erlaubt, wenn die Spalte noch keine Daten enthält.

    Struktur einer Auswahlabfrage
    Im Prinzip besitzt eine Auswahlabfrage eine sehr einfache Struktur und besteht in ihrer Grundform aus zwei bis vier Abschnitten.

    SELECT <auswahlliste>
    	[FROM <tabellenliste>]
    	[WHERE <auswahl- und verknüpfungsbedingungen>]
    	[ORDER BY <sortierbedingungen>]
    

    Der erste Abschnitt ist zwingend und wird mit Hilfe des Befehls SELECT eingeleitet. Danach folgt eine Liste der gewünschten
    Datenfelder und Ausdrücke, d.h., im Rahmen einer solchen Auswahlabfrage können nicht nur Tabellenspalten, sondern auch
    komplexe Berechnungen durchgeführt oder ausgegeben werden.

    SELECT * FROM dozent;
    

    gibt die gesamte Tabelle Dozent aus.

    SELECT d_vorname, d_nachname, d_gebdat FROM dozent;
    

    gibt nur d_vorname, d_nachname, d_gebdat der Tabelle Dozent aus.

    SELECT * FROM dozent ORDER BY d_vorname ASC
    

    gibt die gesamte Tabelle Dozent aufsteigend(ASC) sortiert nach d_vorname aus.

    SELECT * FROM dozent, schule WHERE d_besch=2 AND s_nr=2 ORDER BY d_nachname ASC
    

    Gibt von den Tabellen dozent und schule alles aufsteigend aus, was d_besch=2 und s_nr=2 ist.

    Um einen neuen User in MySQL anzulegen, gibt es zwei Möglichkeiten. Sie melden sich als root bei mysql an, wechseln zur
    Systemdatenbank mysql (USE mysql;) und fügen einen neuen user mittels dem SQL-Befehl INSERT in die user-Tabelle ein:

    INSERT INTO user (user, host, password) VALUES('thomas', 'localhost', PASSWORD('streng_geheim'));
    

    Jetzt gibt es einen neuen User der keine Rechte besitzt. Rechte auf Datenbanken und Tabellen werden mit GRANT
    vergeben und mit REVOKE wieder genommen.
    Rechte für den neu angelegten User thomas mit GRANT:

    GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON weiterbildung TO thomas@localhost;
    

    .
    Damit haben Sie die genannten Rechte an der Datenbank weiterbildung an den User thomas vergeben.
    Das können wir auch einfacher haben. Die bevorzugte Methode ist es, den Befehl GRANT direkt zur Vergabe der Rechte und
    zum Anlegen des Users zu verwenden.

    GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON weiterbildung TO thomas@localhost IDENTIFIED BY 'streng_geheim';
    

    Hiermit haben Sie dann mehrere Fliegen mit einer Klatsche geschlagen. Es wurde ein neuer User, nämlich thomas angelegt, und
    ihm direkt ein Passwort und die Rechte vergeben. Anschließend sollte

    FLUSH PRIVILEGES;/code] aufgerufen werden.
    Passwörter können auch mit SET PASSWORD gesetzt werden.
    [code]SET PASSWORD FOR root=PASSWORD('noch_ein_bisschen_geheimer');
    

    ist eine Alternative zu dem am Anfang vorgestellten mysqladmin-code.

    Mit REVOKE können Sie den Benutzern Ihre Rechte entziehen:

    REVOKE DROP,DELETE ON weiterbildung FROM thomas;
    

    entzieht dem Benutzer thomas die Rechte DROP und DELETE an der Datenbank weiterbildung.

    Sie müssen jetzt nicht unbedingt auf der sehr 'kargen' Eingabeaufforderung von MySQL eine Unmenge von Datenbanken
    und/oder Tabellen erzeugen. Starten Sie einfach Ihren Lieblingseditor und erstellen Sie eine Datenbank mit Tabellen und Werten.
    Z.B.:

    CREATE DATABASE IF NOT EXISTS weiterbildung;
    USE weiterbildung;
    CREATE TABLE IF NOT EXISTS schule(
    	s_nr TINYINT UNSIGNED NOT NULL,
    	s_plz INT UNSIGNED,
    	s_ort VARCHAR(30),
    	s_str VARCHAR(30));
    CREATE TABLE IF NOT EXISTS dozent(
    	d_nr TINYINT UNSIGNED NOT NULL,
    	d_vorname CHAR(20),
    	d_nachname CHAR(20),
    	d_gesch CHAR(1),
    	d_gebdat DATE,
    	d_besch TINYINT UNSIGNED);
    CREATE TABLE IF NOT EXISTS fach(
    	f_name CHAR(20) NOT NULL,
    	f_dauer TINYINT UNSIGNED,
    	f_maxteilnehmer TINYINT UNSIGNED,
    	f_tagsatz DECIMAL(5,2));
    INSERT INTO schule(s_nr, s_plz, s_ort, s_str) VALUES
    (1, 88123, 'Muenchen', 'Schulweg 99'),
    (2, 13629, 'Berlin',  'Schulweg 10');
    INSERT INTO dozent(d_nr, d_vorname, d_nachname, d_gesch, d_gebdat, d_besch) VALUES
    (1, 'Anna', 'Seger', 'w', '1955-04-13', 1),
    (2, 'Peter', 'Hagen', 'm', '1958-10-31', 2),
    (3, 'Werner', 'Lutz', 'm', '1954-06-05', 2),
    (4, 'Christoph', 'Schwarz','m', '1955-02-28', 1);
    INSERT INTO fach(f_name, f_dauer, f_maxteilnehmer, f_tagsatz) VALUES
    ('Englisch', 10, 10, 300),
    ('Russisch', 14, 8, 400),
    ('Japanisch', 20, 12, 500),
    ('Deutsch', 10, 10, 400),
    ('Italienisch', 10, 10, 300);
    

    Speichern Sie das ganze unter weiterbildung.sql ab. Danach führen Sie im Terminal-Fenster oder der Console folgendes aus:

    # mysql -u root -h localhost -p < weiterbildung.sql
    

    -u root -h localhost -p falls Sie schon ein Passwort vergeben haben.

    Natürlich kann auch ein Backup der Datenbank angelegt werden. Dafür gibt es den Befehl mysqldump:

    # mysqldump -u root -h localhost -p weiterbidung > bweiterbildung.sql
    

    Oder alles sichern mit:

    # mysqldump -u root -h localhost -p -Al --all-databases > bweiterbildung2.sql
    

    Wiederherstellen:

    # mysql -u root -h localhost -p < bweiterbildung2.sql
    

    Jetzt können Sie Datenbanken und Tabellen erzeugen und löschen und wissen, wie
    Sie Tabellen verändern, Auswahlabfragen usw. stellen. Später werden noch ein paar SQL-Anweisungen dazukommen.

    MySQL C-API

    Die MySQL C-API wurde in C geschrieben. Sie ist Teil der mysqlclient-Bibliothek. Mit ihr können Sie Client-Programme
    schreiben, die auf den MySQL-Server zugreifen. Es gibt dazu auch ein paar Beispiele im clients-Verzeichnis des MySQL-Quelltextes.

    Wenn MySQL wie oben beschrieben unter Windows installiert wird, ist i.d.R. die Bibliothek im Verzeichnis
    C:\Programme\MySQL\MySQLServer41\lib\opt und die Headerdateien im Verzeichnis C:\Programme\MySQL\MySQLServer41\include vorhanden. Sie müssen dann noch
    die libmysql.dll in ein Systemverzeichnis kopieren. Am besten nach C:\Windows\system32.

    Bei Linux steht die Bibliothek im Verzeichnis /usr/lib, /usr/lib/mysql oder /usr/local/lib/mysql.
    Die Headerdateien im Verzeichnis /usr/include/mysql oder /usr/local/include/mysql. Bei *.rpm's müssen Sie,
    wie oben geschrieben, mysql-devel und mysql-shared installieren (Die Distributoren lassen Grüßen 😉 ).

    Unter Windows gibt es ein Problem, was das Kompilieren eines Client-Programm betrifft. Die libmysql.lib wurde mit dem
    Visual C++ Compiler erstellt. Dieser Compiler vergibt eine eigene Strukturanordnung für die libmysql.dll. Das heißt,
    dass andere Compiler große Probleme bei der Kompilierung haben.

    Beim Borland Compiler BCC55, den Sie hier herunterladen können,
    gibt es das Tool coff2omf. Damit können Sie die im Microsoft-Croff-Format vorliegende libmysql in ein für Borland übliches Format konvertieren:

    coff2omf C:\Programme\MySQL\MySQLServer41\lib\opt\libmysql.lib C:\borland\bcc55\lib\libmysql.lib
    

    Jetzt haben Sie gleichzeitig auch die Bibliothek in das Bibliotheksverzeichnis des Borland-Kompilers kopiert.
    Nutzer anderer Compiler werden wohl mit keinem der beiden Formate zurechtkommen. Entweder Sie steigen auf einen der beiden Compiler um,
    oder Sie kompilieren den Sourcecode für diese Bilbliothek selbst.

    Kommen wir nun zum Lcc-win32 Compiler. Konvertieren der libmysql.lib:
    Starten Sie über Start->Programme->Lcc-win32->Lcc-win32 oder auf der Kommandoebene wedit. Klicken Sie auf Utils->import foreign library.

    Jetzt klicken Sie auf den Button mit den drei Punkten rechts neben dem Eingabefeld Path of the foreign library(microsoft format) to import und suchen die
    libmysql.lib im C:\Programme\MySQL\MySQLServer41\lib\opt-Verzeichnis von MySQL.

    Das Textfeld Path for the resulting library wird dann automatisch ausgefüllt. Den Rest können Sie so lassen. Jetzt auf ok klicken.

    Nachdem Sie auch hier diese beiden Meldungen bestätigt haben, können Sie die Bibliothek benutzen.

    Das können Sie auch auf der Kommandoebene machen, indem Sie die Programme pedump und buildlib benutzen. Mit

    pedump /exp C:\Programme\MySQL\MySQLServer41\lib\opt\libmysql.lib
    

    erstellt pedump eine Datei libmysql.exp im Verzeichnis C:\lcc\buildlib.

    buildlib C:\lcc\buildlib\libmysql.exp C:\lcc\lib\libmysql.lib
    

    Jetzt ist die libmysql.lib im lib-Verzeichnis vom Lcc-Compiler und Sie können sie benutzen.

    Hier erst mal ein sehr einfaches Programm, für eine SELECT-Abfrage an den MySQL-Server.

    /*
    	select.c - SELECT-Abfrage
    
    	für VC++ und Lcc-win32
    	#include "C:\Programme\MySQL\MySQLServer41\include\mysql.h"
    */
    #include <mysql.h>
    #if defined __WIN32__
    	#include <windows.h>
    #endif 
    #include <stdio.h>
    
    int main(void) {
    	MYSQL		*connection;	/* Der MySQL-Handler: Verbindung zum MySQL-Server */
    	MYSQL_RES	*ergebnis;	/* Ergebnis einer Abfrage */
    	char		abfrage[1000];	/* beinhaltet die Abfrage */
    	MYSQL_ROW	zeile;		/* Ergebniszeile */
    	int 		anz;		/* Anzahl der Zeilen */
    	int 		i;
    
    	connection = mysql_init(NULL);		/* Datenstruktur initalisieren */
    
    	/* Die einzelnen Typen von mysql_real_connect() werden weiter unten erklärt */ 
            /* Später werden wir natürlich Variablen für Host, User, Passwort usw benutzen */
    	if(mysql_real_connect(			/* Verbindung aufbauen */
    		connection,			/* MySQL-Handler */
    		"localhost",			/* Host-Name */
    		"thomas",			/* User-Name */
    		"streng_geheim",		/* Passwort  */
    		"weiterbildung",		/* Datenbank */
    		0,				/* Port, standard = 0 */
    		NULL,				/* Socket, standard = NULL */
    		0) == NULL) {			/* keine Flags */
    			fprintf(stderr, "Verbindung schlug fehl: %u (%s)\n", mysql_errno(connection), mysql_error(connection));
    			mysql_close(connection);
    			exit(1);
    		}
    
    	/* muss hier erst mal genügen */
    	sprintf(abfrage, "SELECT * FROM dozent");	/* Ich weiss, ziemlich krude */
    
    	if((mysql_query(connection, abfrage) != 0)) { 	/* SQL-Befehl ausführen */
    		fprintf(stderr, "Abfrage schlug fehl: %u (%s)\n", mysql_errno(connection), mysql_error(connection));
    		mysql_close(connection);
    		exit(1);
    	}
    
    	ergebnis = mysql_store_result(connection);	/* Ergebnisse speichern */
    
    	anz = mysql_num_fields(ergebnis);		/* Anzahl der Zeilen ermitteln */
    	while((zeile = mysql_fetch_row(ergebnis))) {	/* Zeilen aus ergebnis abholen */
    		for(i = 0; i < anz; i++)		
    			printf("%s ", zeile[i]);	/* Zeilen ausgeben */
    		printf("\n");
    	}
    
    	mysql_free_result(ergebnis);			/* Ergebnisspeicher wieder freigeben */
    
    	mysql_close(connection);			/* verbindung trennen */
    	return 0;
    }
    

    Mit VC++ jetzt kompilieren, aber noch nicht linken. Vorher noch den Pfad zur mysql-Bibliothek
    dem Linker mitteilen und zwar über:
    Projekt->Einstellungen dort im Register Linker ans Ende des Feldes Objekt-/Bibliothek-Module
    c:\mysql\lib\opt\libmysql.lib hinzufügen.
    Jetzt das Programm linken und ausführen.

    Mit BCC55 eine Objekt-Datei erstellen:

    bcc32 -c -I"C:\Programme\MySQL\MySQLServer41\include" select.c
    

    Jetzt linken:

    ilink32 c0x32.obj select.obj , select.exe , , import32.lib cw32.lib libmysql.lib
    

    Naja, vielleicht sollten Sie eine Batch-Datei dafür erstellen ;).
    bcc_link.bat

    ilink32 c0x32.obj %1 , %2 , , import32.lib cw32.lib libmysql.lib
    

    %1 = OBJ-Datei
    %2 = EXE-Datei
    Aufruf z. B.:

    bcc_link select.obj select.exe
    

    Mit dem Lcc-win32:

    lcc select.c
    

    die Objekt-Datei erstellen und dann mit

    lcclnk select.obj -L"C:\lcc\lib" libmysql.lib
    

    die exe-Datei - nicht vergessen #include "C:\Programme\MySQL\MySQLServer41\lib\opt\mysql.h" einbinden.

    Unter Linux erstellen Sie das Programm folgendermaßen mit

    # gcc -c -I/usr/include/mysql select.c
    

    erhalten Sie eine Object-Datei namens select.o. Dann mit

    # gcc -o select select.o -I/usr/lib/mysql -lmysqlclient
    

    linken und ./select ausführen.
    Wenn alles gut gegangen ist, erhalten Sie das Ergebnis der SELECT-Abfrage auf dem Bildschirm.
    Die MySQL C-API verwendet auch Funktionen aus der Headerdatei <math.h>. Bei manchen Distributionen dann noch -lm hinzulinken.

    # gcc -o select select.o -I/usr/lib/mysql -lmysqlclient -lm
    

    Bevor ich jetzt weitermache, noch ein paar Erklärungen für einige Datentypen in der Datei oben:

    MYSQL
    Der MySQL-Handler wird benutzt, um eine Verbindung mit der Datenbank herzustellen. Er ist in mysql.h definiert.

    MYSQL_RES
    stellt das Ergebnis einer Abfrage, die Zeilen zurückgibt, dar. Also SELECT oder SHOW.
    MYSQL_ROW
    ist eine typsichere Darstellung einer Zeile von Daten. Sie ist als ein Array von Strings implementiert.
    Sie können diese nicht als NULL-begrenzte Zeichenketten behandeln, da sie binäre Daten enthalten können, die
    intern NULL-Bytes beinhalten können. Zeilen werden mit dem Aufruf mysql_fetch_row() abgeholt.

    my_ulonglong
    wird für die Anzahl der Zeilen und für mysql_affect_rows(), mysql_num_rows() und mysql_insert_id() verwendet. Der
    Bereich ist von 0 bis 1.84e19. Bei manchen Systemen funktioniert es nicht, einen Wert von diesem Typ auszugeben.
    Dann sollte man ihn nach unsigned long umwandeln und %lu Format von printf() verwenden.

    printf("Anzahl der Zeilen: %lu\n", (unsigned long) mysql_num_rows(zeilen));
    
    MYSQL *mysql_init(MYSQL *connection)
    

    hiermit wird die MYSQL-Strukturvariable connection initialisiert, bevor eine Verbindung zum Datenbank-Server hergestellt wird.
    Um die Verbindung herzustellen, wird die folgende Funktion benutzt.

    MYSQL *mysql_real_connect(
    /* Adresse einer existierenden MYSQL-Struktur */
    MYSQL *connect, 
    
    /* Der Wert von host kann entweder ein Hostname oder eine IP-Adresse sein.
       Wenn host NULL oder 'localhost' ist, wird eine Verbindung zum lokalen 
       Host angenommen. Wenn das Betriebssystem Sockets (Unix) oder Named Pipes (Windows)
       unterstützt, werden diese statt TCP/IP benutzt, um sich mit dem Server zu verbinden. */
    const char *host,
    
    /* user enthält die MySQL-Login-Benutzerkennung. Wenn user NULL ist, wird der aktuelle
       Benutzer angenommen. Unter Unix ist das der aktuelle Login-Name. Unter Windows-ODBC
       muss der aktuelle Benutzername explizit angegeben werden. */
    const char *user,
    
    /* Der passwd-Parameter enthält das Passwort für user. Wenn passwd NULL ist, werden nur
       Einträge in der user-Tabelle für Benutzer auf Übereinstimmung überprüft, die ein leeres
       Passwort-Feld haben. Versuchen Sie nicht das Passwort zu verschlüsseln, bevor Sie
       mysql_real_connect() aufrufen. Die Verschlüsselung wird automatisch durch die Client-API
       gehandhabt */
    const char *passwd,
    
    /* db ist der Datenbankname. Wenn db nicht NULL ist, wird die vorgabemäßige Datenbank für
       die Verbindung auf diesen Wert gesetzt. */
    const char *db,
    
    /* Wenn port nicht 0 ist, wird dieser Wert als Port-Nummer für die TCP/IP-Verbindung benutzt.
       Achtung: der host-Parameter legt den Verbindungstyp fest. */
    unsigned int port,
    
    /* Wenn unix_socket nicht NULL ist, legt die Zeichenkette den Socket oder die Named Pipe fest, 
       die benutzt werden sollen. Auch hier legt der host-Parameter den Vebindungstyp fest.
    const char *unix_socket,
    
    /* Der Wert von client_flag ist üblicherweise 0. Die Flags werden in der MySQL-Referenz beschrieben.
       Wir werden sie hier nicht benutzen. */
    unsigned int client_flag);
    

    *unsigned int mysql_errno(MYSQL connection)
    Gibt den Fehler-Code für die zuletzt aufgerufene API-Funktion zurück, die erfolgreich sein
    oder fehlschlagen kann. Ein Rückgabewert von 0 bedeutet, dass kein Fehler auftrat.

    char *mysql_error(MYSQL *connection)
    Gibt für die von mysql angegebene Verbindung die Fehlermeldung für die zuletzt aufgerufene API-Funktion zurück,
    die erfolgreich sein oder fehlschlagen kann. Eine leere Zeichenkette "" bedeutet, dass kein Fehler auftrat.
    Folgende zwei Tests sind demnach äquivalent:

    if(mysql_errno(connection))  {
    	/* Ein Fehler trat auf
    }
    
    if(mysql_error(connection)[0] != '\0') {
    	/* Ein Fehler trat auf
    }
    

    Mit char *abfrage (char abfrage[1000]) können wir eine Abfrage machen und alle SQL-Sätze bilden. Eine Abfrage wird mit der folgenden
    Funktion durchgeführt:

    int mysql_query(MYSQL *connection, const char *abfrage);
    

    ausgeführt. mysql_query() kann nicht für Abfragen benutzt werden, die Binärdaten enthalten. Hierfür gibt es

    int mysql_real_query(MYSQL *connection, const char *abfrage, unsigned int length);
    

    length ist die Länge des Strings abfrage. mysql_real_query() wird benutzt, wenn eine Abfrage Binärdaten enthält. Binärdaten können das
    '\0'-Zeichen enthalten. mysql_real_query() ist schneller als mysql_query(), weil es in der Abfragezeichenkette nicht strlen() aufruft.

    MYSQL_RES
    repränsentiert das Ergebnis einer Abfrage, die Zeilen zurückgibt

    MYSQL_RES *ergebnis
    
    ergebnis = mysql_use_result(connection)
    

    Diese Funktion wird für jede Abfrage aufgerufen, die erfolgreich Daten zurückgibt(SELECT,SHOW,DESCRIBE,EXPLAIN).
    Sie liest die Ergebnismenge nicht tatsächlich in den Client. Stattdessen muss jede Zeile individuell abgerufen werden,
    indem Aufrufe von mysql_fetch_row() durchgeführt werden. das liest das Ergebnis einer Anfrage direkt vom Server, ohne
    es in einer temporären Tabelle oder einem lokalem Puffer zu speichern.

    MYSQL_RES *mysql_store_result(MYSQL *connection)
    

    liest das gesamte Ergebnis einer Abfrage zum Client ein, weist eine MYSQL_RES-Struktur zu und platziert des Ergebnis
    in diese Struktur.

    MYSQL_ROW *mysql_fetch_row(MYSQL_RES *ergebnis)
    

    ruft die nächste Zeile der Ergebnismenge ab. Wenn sie nach mysql_use_result() benutzt wird, gibt mysql_fetch_row NULL zurück,
    wenn es keine Zeilen mehr zum Abruf gibt oder wenn ein Fehler auftrat.

    Die Verbindung zur Datenbank wird am Ende mit

    mysql_close(connection);
    

    beendet.

    Noch ein paar Funktionen:

    unsigned int mysql_num_fields(MYSQL *connection)
    

    gibt die Anzahl der Spalten in einer Datenbank zurück.

    my_ulonglong mysql_num_rows(MYSQL_RES *ergebnis);
    

    die Anzahl der Zeilen eines Abfrageergebnisses.

    my_ulonglong mysql_affect_rows(MYSQL *connection);
    

    gibt die Anzahl der Zeilen zurück, die von einer INSERT, DELETE oder UPDATE Abfrage geändert wurden.

    Vielleicht möchten Sie ja auch die Überschriften der einzelnen Spalten der Tabelle auslesen und anzeigen. Das erreichen Sie mit
    der Struktur MYSQL_FIELD und der Funktion mysql_fetch_fields().

    int main(void) {
    	MYSQL		*connection;	/* Der MySQL-Handler: Verbindung zum MySQL-Server */
    	MYSQL_RES	*ergebnis;	/* Ergebnis einer Abfrage */
    	char		abfrage[1000];	/* beinhaltet die Abfrage */
    	MYSQL_ROW	zeile;		/* Ergebniszeile */
    	MYSQL_FIELD	*felder;	/* Informationen über die Felder einer Tabelle */
    	int 		anz;		/* Anzahl der Zeilen */
    	int 		i;
    
    	... 
    
    	ergebnis = mysql_store_result(connection);	/* Ergebnisse speichern */
    	felder = mysql_fetch_fields(ergebnis);		/* Informationen über die Felder auslesen*/
    	anz = mysql_num_fields(ergebnis);		/* Anzahl der Zeilen ermitteln */
    	for(i = 0; i < anz; i++)			/* Überschriften ausgeben */
    		printf("%12s", felder[i].name);
    	printf("\n");
    	while((zeile = mysql_fetch_row(ergebnis))) {	/* Zeilen aus ergebnis abholen */
    		for(i = 0; i < anz; i++)		
    			printf("%12s ", zeile[i]);	/* Zeilen ausgeben */
    		printf("\n");
    	}
    
    	...
    }
    

    Bei SQL gibt es zwei grundsätzlich verschiedene Arten von Abfragen. Die eine haben Sie oben kennengelernt. Abfragen mit
    SELECT, SHOW usw. geben ein Ergebnis zurück, nämlich den Inhalt oder ein Teil des Inhaltes einer Tabelle oder Datenbank
    in Form von Zeilen, oder Datensätze auch Tupel genannt.

    Mit UPDATE, INSERT oder DELETE bekommen Sie keine Daten zurück.
    Jetzt werden wir eine solche Abfrage in C programmieren. Der Aufbau ist sehr ähnlich dem ersten.

    /*
    	update.c - UPDATE-Abfrage
    
    	für VC++ und Lcc-win32
    	#include "c:\Programme\MySQL\MySQLServer41\include\mysql.h"
    */
    #include <mysql.h>
    #if defined __WIN32__
    	#include <windows.h>
    #endif 
    #include <stdio.h>
    
    int main(void) {
    	MYSQL		*connection;	/* Der Mysql-Handler: Verbindung zum MySQL-Server */
    	char		abfrage[1000];	/* beinhaltet die Abfrage */
    	int 		i;
    	unsigned long	anzahl;		/* Anzahl der veränderten Datensätze */
    
    	connection = mysql_init(NULL);		/* Datenstruktur initalisieren */
    	if(mysql_real_connect(			/* Verbindung aufbauen */
    		connection,			/* MySQL-Handler */
    		"localhost",			/* Host-Name */
    		"thomas",			/* User-Name */
    		"streng_geheim",		/* Passwort  */
    		"weiterbildung",		/* Datenbank */
    		0,				/* Port, standard = 0 */
    		NULL,				/* Socket, standard = NULL */
    		0) == NULL) {			/* keine Flags */
    			fprintf(stderr, "Verbindung schlug fehl\n");
    			mysql_close(connection);
    			exit(1);
    		}
    
    	sprintf(abfrage, "UPDATE fach SET f_tagsatz=300 WHERE  f_tagsatz=350;");
    
    	if((mysql_query(connection, abfrage) != 0)) { 	/* SQL-Befehl ausführen */
    		fprintf(stderr, "Abfrage schlug fehl (%s): %s\n", abfrage, mysql_error(connection));
    		mysql_close(connection);
    		exit(1);
    	}
    
    	if((anzahl = (unsigned long)mysql_affected_rows(connection)) <= 0)	/* Hier wird die Anzahl der geänderten */
    		printf("Kein Datensatz geändert\n");				/* Datensätze abgeholt und ausgegeben */
    	else
    		printf("%d %s geändert\n", anzahl, (anzahl==1)?"Datensatz wurde ":"Datensätze wurden ");
    
    	mysql_close(connection);			/* verbindung trennen */
    	return 0;
    }
    

    Das Programm sollte die Anzahl der geänderten Datensätze ausgeben.

    Das Ganze ist eigentlich gar nicht so schwierig. Grundsätzlich läuft es meistens nach dem gleichen Schema ab:

    • mysql_init() Datenstruktur initialisieren

    • mysql_real_connect() Verbindung aufbauen

    • mysql_query() oder mysql_real_query() Abfrage absetzen

    • gibt es eine Rückgabe?

    • ja
      mysql_store_result() das Ergebnis vom Typ MYSQL_RES abholen
      mysql_num_fields() Anzahl der Spalten abholen
      mysql_fetch_row() Zeilen auslesen
      mysql_free_result() Speicher freigeben

    • nein
      mysql_affected_rows() Anzahl der gefundenen Datensätze angeben

    • mysql_close() Verbindung beenden

    • mysql_server_end();

    Literatur

    Hauptsächlich die MySQL-Documentation
    und

    SQL | ISBN: 3827319900



  • Sehr schönes Tutorial!

    Eine kleine Anmerkung:

    Um mit gcc -lmysqlclient unter Linux compilieren zu können, benötigt man die mysql database development files (libmysqlclient-dev).
    Mit apt bzw. aptitude lässt sich die momentan neuste Version zum Beispiel so installieren (vorausgesetzt in der sources.list sind die notwendigen Einträge für MySQL):

    apt

    apt-get install libmysqlclient15-dev
    

    aptitude

    aptitude install libmysqlclient15-dev
    

    Ich denke es wäre ganz sinnvoll dies hinzuzufügen, damit nicht so lange nach der Lösung gesucht werden muss, wenn libmysqlclient-dev noch nicht installiert sein sollte.

    Weiter so!

    lg Max


Anmelden zum Antworten