IT-Academy Logo
Sign Up Login Help
Home - Programmieren - Datenbanken - Datenbanken



Datenbanken

Grundsätzliche Infos über Datenbanken. Welche gibt es, wofür braucht man sie...


Autor: Arnold Willerner (willemer)
Datum: 23-01-2002, 19:36:05
Referenzen: http://www.willemer.de/informatik/index.htm
Schwierigkeit: Profis
Ansichten: 8941x
Rating: 9.43 (7x bewertet)

Hinweis:

Für den hier dargestellte Inhalt ist nicht der Betreiber der Plattform, sondern der jeweilige Autor verantwortlich.
Falls Sie Missbrauch vermuten, bitten wir Sie, uns unter missbrauch@it-academy.cc zu kontaktieren.

[Druckansicht] [Als E-Mail senden] [Kommentar verfassen]



Relationale Datenbanken

Relationen

Codd bezeichnet Datenbanken als "minimal relational", wenn sie folgende Bedingungen erfüllen.
  1. Die Informationen werden einheitlich in Form von Tabellen repräsentiert.
  2. Der Anwender sieht keine Verweisstrukturen zwischen den Tabellen.
  3. Es gibt mindestens die Operation der Selektion, der Projektion und des JOIN definiert.

Schlüssel und funktionale Abhängigkeit

Der Schlüssel einer Tabelle bestimmt eindeutig eine Zeile der Tabelle. Man spricht auch davon, dass die Felder, die nicht im Schlüssel vorkommen, von diesem funktional abhängig sind.

Referentielle Integrität

Felder können Werte enthalten, die in anderen Tabellen Schlüssel bilden. Werden sie zur Referenz auf andere Tabellen verwendet, spricht man von einem Fremdschlüssel. Die referentielle Integrität fordert, dass für jeden Fremdschlüssel der Datensatz existiert, der referenziert wird. Das bedeutet, dass die Datenbank verhindern muss, dass ein Satz gelöscht wird, auf den noch ein Fremdschlüssel verweist.

Normalisierung

Die Normalisierung dient in erster Linie des Abbaus der Redundanzen. J.W.Schmidt (S.38) definiert:
"Relationen, deren sämtliche Nicht-Schlüsselattribute funktional vom Schlüssel abhängen und weitere funtionale Abhängigkeiten nicht besitzen, nennt man normalisiert."

Es ergeben sich die Folgerungen:

Eine Tabelle sollte einen Primärschlüssel haben
Eine Tabelle sollte nur Daten für einen Typ einer Entität aufnehmen
Eine Tabelle sollte Spalten vermeiden, deren Elemente leer sein können
Eine Tabelle sollte keine wiederholenden Werte haben.
Nicht immer ist es in der Praxis sinnvoll, eine Datenbank vollständig zu normalisieren. Wird die Aufspaltung in zu viele Tabellen erforderlich, kann es Performance-Einbrüche geben.

Entity Relationship

Zum Design einer Datenbank dient die Bildung eines Datenmodells. Das populärste Werkzeug ist das Entitiy Relationship Model. Der etwas vereinfachende Ablauf ist, dass für alle Objekte und Beziehungen je eine Tabelle erstellt wird.

Beispiel

Soll der Verkauf von Artikeln abgebildet werden, hat man zwei Entitäten: Kunden und Artikel. Zwischen beiden gibt es eine Beziehung (Relation): der Kauf. Damit haben wir drei Tabellen: Kunde, Artikel und Kauf. Die Tabelle Kauf enthält minimal die Fremdschlüssel von Kunde und Artikel.

Transaktionen

Eine Transaktion führt eine Datenbank von einem konsistenten Zustand immer in einen konsistenten Zustand. Das bedeutet, sie wird entweder vollständig oder gar nicht ausgeführt. Wird im Zuge der Transaktion festgestellt, dass sie teilweise nicht ausgeführt werden kann, wird der Rollback eingeleitet. Dieser sorgt dafür, dass die Datenbank in dem Zustand kommt, in dem sie von der Transaktion vorgefunden wurde. Ist jeder Teilaspekt der Transaktion einwandfrei durchlaufen, wird per Commit die Änderung endgültig gemacht.

Joins

Ein Join ist eine Zusammenfassung zweier Tabellen.

Karthesisches Produkt

Das karthesische Produkt ist ein Join, der nicht beschränkt ist. Etwas lässig formuliert, könnte man sagen: Jeder gegen jeden.

SELECT KUNDE.NAME, KUNDE.ADRESSE, BETREUER.NAME
FROM KUNDE,BETREUER;

Es entsteht eine Tabelle, in der jeder Kunde mit jedem Betreuer gekreuzt wird.

Equi-Join

Beispielsweise will man eine Liste aller Kunden mit dem Namen ihrer Betreuer. Die Tabelle BETREUER enthält als Primärschlüssel ID. Die Tabelle KUNDE enthält die Spalte BETREUERID.

SELECT KUNDE.NAME, KUNDE.ADRESSE, BETREUER.NAME
FROM KUNDE,BETREUER
WHERE BETREUER.ID=KUNDE.BETREUERID;

Dieser Join heisst Equi-Join, weil er auf der Gleichheit zweier Spalten basiert. In diesem Fall wird also der Name des Betreuers angezeigt, der zu jedem Kunden gehört.

Literatur

P.C.Lockemann u. J.W.Schmidt (Hrsg)
Datenbank-Handbuch, Springer, 1987.

Notizen zu SQL

Data Definition Language (DDL)

Tabellen

Anlegen einer Tabelle

Eine Datenbank besteht aus mehreren Tabellen, die zunächst einmal erzeugt werden müssen.

CREATE TABLE <TabellenName> (
  [ PRIMARY KEY ( <FeldNamensListe> ), ]
  [ FOREIGN KEY ( <FeldName> ) REFERENCES <TabellenName>, ]*
  [ UNIQUE ( <FeldNamensListe> ), ]
  <FeldDeklarationen>
)

Die Verwendung des Schlüsselwortes UNIQUE an dieser Stelle wird nicht von allen Datenbanken unterstützt. In solchen Fällen verwendet man einen INDEX mit dem Attribut UNIQUE.

Löschen einer Tabelle

Mit DROP TABLE <TabellenName> wird eine Tabelle wieder gelöscht.

Ändern der Tabelle

Das Ändern einer Tabelle sollte vermieden werden, kann aber durch den Befehl ALTER TABLE erreicht werden. Nicht alle Datenbanken unterstützen diesen Befehl.

ALTER TABLE <TabellenName> DROP <Spalte>
ALTER TABLE <TabellenName> ADD <FeldDeklarationen>
ALTER TABLE <TabellenName> MODIFY <FeldDeklaration>

Mit DROP wird eine Spalte wieder gelöscht. Mit ADD wird eine oder mehrere Spalten hinzugefügt. Mit MODIFY wird eine Spalte in ihren Datentyp geändert.

Index

Ein Index ist schneller Zugriffspfad auf die Daten. Durch das Wort UNIQUE wird gewährleistet, daß der Inhalt für die Tabelle eindeutig ist.

CREATE [UNIQUE] INDEX <Indexname> 
    ON <TabellenName> ( <FeldNamensListe> );

Key (Primary und Foreign)

Ein Key bietet die Möglichkeit, einzelne Sätze aus anderen Tabellen zu referenzieren. Die Zieltabelle muss einen Primary Key besitzen, über den die Zeilen eindeutig bestimmt werden können. Er wird mit dem CREATE TABLE definiert oder kann mit ALTER TABLE nachdefiniert werden. Ein Primary Key erfordert immer einen unique Index.

Auf den Primary Key kann sich der Foreign Key einer anderen Tabelle beziehen. In dieser Spalte befindet sich der Wert des Primary Key der Zieltabellenzeile, auf den referenziert werden soll. Bevor ein Foreign Key gesetzt wird, muss der Primary Key der Zieltabelle definiert sein.

ALTER TABLE <TabellenName>  { [ADD] | DROP } 
    PRIMARY KEY ( <FeldNamensListe> );
ALTER TABLE <TabellenName>  { [ADD] | DROP } 
    FOREIGN KEY <KeyName> ( <FeldName> ) REFERENCES <TabellenName>;

Ansichten (Views)

Mit CREATE VIEW kann eine Tabelle eingeschränkt werden. und damit verschiedenen Benutzern nur Teile der Datenbank zur Verfügung gestellt werden.

CREATE VIEW <ViewName> [ (<FeldNamensListe>) ]
  AS SELECT <Select-Parameter> ;

Wird die FeldNamensListe angegeben, werden die Spaltennamen des FROM-Attribut auf diese Namen umgesetzt.

Data Manipulation Language (DML)

INSERT

INSERT INTO <TabellenName> ( <FeldNamensListe> )
  VALUES (<Wert> [, <Wert>]* );

Es kann durch Kombination mit einer Abfrage auch der Inhalt einer oder mehrerer anderer Tabellen eingefügt werden.

INSERT INTO <Tabellenname> [ ( <Feldname> [, <Feldname]* ) ]
  SELECT <Select-Parameter> ;

UPDATE

UPDATE <TabellenName> SET <FeldName>=<Wert> [,<FeldName>=<Wert>]* <WhereKlausel>

DELETE

DELETE FROM <TabellenName> <WhereKlausel>

Anfragen: SELECT

Eine Abfrage wird durch den Befehl SELECT realisiert. Die Besonderheiten liegen in seinen Parametern.

SELECT {<FeldNamensListe> | '*' } FROM <TabellenName> [ <WhereKlausel> ] ;

WHERE-Klausel

WHERE wird benutzt um eine Selektion zu realisieren, also eine Einschränkung der Zeilenzahl.

	WHERE <Bedingung>

Das WHERE kennt neben =, <, >, <>, <= und >= die Vergleichsoperatoren:

LIKE enthält die Zeichenkette. Dabei gelten die Wildcards _ für ein
Zeichen und % für eine beliebige Zeichenkette.
BETWEEN der Wert liegt zwischen zwei Werten. Beispiel: Wert BETWEEN 'A' AND 'Z'
IS NULL der Wert ist bisher nicht besetzt worden. Beispiel: Wert IS NULL

Herstellerspezifisches

Die Systemtabellen

Die Anzeige der Datenbankstruktur erfolgt durch Abfrage der Data Dictionary Tabellen. Es handelt sich um ganz normale Abfragebefehle, die auf die Systemtabellen angewendet werden. Die Namen und die Struktur der Tabellen sind von Hersteller zu Hersteller verschieden.

Oracle

SELECT * FROM USER_TABLES;
SELECT * FROM ALL_TABLES;
SELECT * FROM DBA_TABLES;

gupta/Centura

SELECT * FROM systables;                 
zeigt alle angelegten Tabellen der DB SELECT * FROM syscolumns WHERE tbname='ORDERS';
zeigt alle Spalten der Tabelle ORDERS

Ex- und Import der Datenbankdaten

gupta / Centura

UNLOAD  {  ASCII | DIF | [ DATA ] SQL } 
<DateiName> [ OVERWRITE ] <TabellenName> LOAD { ASCII | DIF | SQL } <DateiName> ;

Mit dem Schlüsselwort ASCII werden reine Textdateien mit den Tabelleninhalten erzeugt. DIF ist ein Standard zum Austausch von Tabellen zwischen Datenbanken und Spreadsheets und enthält keine Strukturinformation. SQL erzeugt eine Datei, die SQL-Befehle enthält, die die Tabelle mit allen Inhalten erzeugen.

Beim Export (UNLOAD) sind nur Tabellen, nicht aber VIEWs zulässig. Auch das Anhängen von WHERE ist nicht möglich.

<DateiName> bezeichnet einen Dateinamen, wie er im Betriebssystem des lokalen Rechners üblich ist.

Definitionen

FeldDeklarationen::
<Feldname> <Typ> [NOT NULL] [ , <Feldname> <Typ> [NOT NULL] ]*

FeldNamensListe::
<Feldname> [ ,<Feldname> ]*

Eine durch Komma getrennte Liste von Feldnamen.

Typ::
Der Datentyp kann einer der folgenden sein:

CHAR(<n>) Zeichenkette
NUM [(<StellenZahl> [, <NachKommaStellen>])] ganzzahlig
FLOAT Fließkomma
DATE Datum: Konstante: 12/24/1999

TabellenName
Eine Tabelle, ein View oder eine virtuelle Tabelle, die durch eine Abfrage gebildet wird.

Notation der Befehle

 <xxx> 
xxx ist durch etwas anderes zu füllen, beispielsweise Namen oder Werte
 [xxx] 
xxx kann auftreten, muß aber nicht.
 [xxx]* 
xxx kann nicht oder mehrfach auftreten.
 { xxx | yyy | zzz } 
Es muß exakt eines von xxx oder yyy oder zzz auftreten.
 'x' 
Hier ist das Zeichen x, nicht das Metazeichen x gemeint. Bsp.: '*' meint das Zeichen *

ODBC - Open Database Connectivity

ODBC ist ein Standard unter Windows, der von Microsoft erstellt wurde, um die Programmierung von Datenbanken zu vereinheitlichen.

Der erste Schritt besteht in der Verbindungsaufnahme, der die Parameter Datenbankname, User und Kennwort benötigt. Aus der Sicht des Programmes sollte es dabei gleich sein, ob die Datenbank lokal oder auf einem Server im Netz steht. Um dies zu erreichen, wird auf dem Client ein ODBC-Treiber für jede Datenbank eingerichtet, die zugegriffen werden soll.

Der Zugriff auf die Datenbank wird mit SQL-Befehlen realisiert, die der Treiber umzusetzen bzw. durchzureichen hat.

Literatur

Leinecker, Richard C. / Archer, Tom
Die Windows 98 Programmierbibel, Thomson, Bonn, 1998. S.629-722.
Microsoft
MSDN Library, July 1999.
Simon, Richard J.
Multimedia, ODBC & Telefonie. Windows 95/Windows NT API Bible, Bd. 3, SAMS, Haar b. München, 1997.

ODBC-Programmierung per API

Header-Dateien

Ein Modul, das ODBC-Aufrufe macht, braucht die Headerdateien sql.h, sqlext.h und in einigen Fällen sqltypes.h.

Funktionen zum An- und Abmelden

SQLAllocHandle und SQLFreeHandle

SQLRETURN SQLAllocHandle(
     SQLSMALLINT HandleTyp, 
     SQLHANDLE  InHandle, 
     SQLHANDLE *OutHandle); 

SQLRETURN SQLFreeHandle(   
     SQLSMALLINT HandleTyp, 
     SQLHANDLE FreeHandle); 
HandleTyp
Der HandleTyp ist einer der folgenden:

SQL_HANDLE_ENV Ein Environmenthandle wird gesucht
SQL_HANDLE_DBC Es wird ein Verbindungshandle angelegt
SQL_HANDLE_STMT
SQL_HANDLE_DESC
InHandle
Das InHandle ist das Handle, das als Basis gebraucht wird, um ein neues Handle zu erlangen.
Wenn HandleTyp SQL_HANDLE_ENV ist, braucht man kein InHandle und der Parameter wird mit SQL_NULL_HANDLE belegt. Ist HandleType SQL_HANDLE_DBC, wird das Environment-Handle übergeben. Ist HandleTyp SQL_HANDLE_STMT oder SQL_HANDLE_DESC wird das Verbindungs-Handle gebraucht.
OutHandle
Hier wird die Adresse der Variablen übergeben, die das Handle aufnehmen soll.
FreeHandle
Das Handle, das freigegeben werden soll.

Bemerkung

Die Funktion SQLAllocHandle löst in der Version 3 die Funktionen SQLAllocConnect, SQLAllocEnv und SQLAllocStmt ab. Die Funktion SQLFreeHandle löst in der Version 3 die Funktionen SQLFreeConnect, SQLFreeEnv und SQLFreeStmt ab.

SQLConnect

SQLRETURN SQLConnect(
     HDBC hDBC,
     UCHAR *strDSN,
     SWORD lenDSN,
     UCHAR *strUSER,
     SWORD lenUSER,
     UCHAR *strPASSWD,
     SWORD lenPASSWD);

hDBC
Das Handle
strDSN
Der Name der Datenquelle
lenDSN
Die Länge des Namen der Datenquelle. SQL_NTS gibt an, dass der String nullterminiert ist
strUSER
Benutzerkennung
lenUSER
Die Länge der Benutzerkennung. SQL_NTS gibt an, dass der String nullterminiert ist
strPASSWD
Das Kennwort
lenPASSWD
Die Länge des Kennworts. SQL_NTS gibt an, dass der String nullterminiert ist
Verbindet mit einer Datenquelle.

Beispiel

HENV hEnv;
HDBC hDBC;

  SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);  
  SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)
SQL_OV_ODBC3, SQL_IS_INTEGER); SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDBC); if (hDBC) { retCode = SQLConnect(hDBC, (UCHAR *)"testdb", SQL_NTS, (UCHAR *)"adm",SQL_NTS,(UCHAR *)"adm",SQL_NTS); ... SQLDisconnect(hDBC); SQLFreeHandle(SQL_HANDLE_DBC, hDBC); } SQLFreeHandle(SQL_HANDLE_ENV, hEnv);

Funktionen zur Ausführung von Anweisungen

Für die Ausführung von Anweisungen wird ein Handle benötigt, das durch SQLAllocHandle und SQLFreeHandle angelegt und freigegeben wird.

HDBC hDBC;
HSTMT hStmt;

SQLAllocHandle(SQL_HANDLE_STMT, hDBC, &hStmt)
. . .
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);

SQLPrepare, SQLExecute und SQLExecDirect

Eine Anweisung wird mit SQLPrepare zunächst übersetzt. Der Statementhandle hält dann diese Anweisung, die durch den Aufruf von SQLExecute auch mehrmalig ausgeführt werden kann.

SQLRETURN SQLPrepare(   
     SQLHSTMT StatementHandle, 
     SQLCHAR * StatementText, 
     SQLINTEGER TextLength); 

SQLRETURN SQLExecute(
     SQLHSTMT     StatementHandle);

Wird die Anweisung nur einmal ausgeführt, kann man beides in SQLExecDirect zusammenfassen.

SQLRETURN SQLExecDirect(   
     SQLHSTMT StatementHandle, 
     SQLCHAR * StatementText, 
     SQLINTEGER TextLength); 

StatementHandle
Das Handle des Kommandos
StatementText
Die SQL-Anweisung
TextLength
Die Länge der SQL-Anweisung

SQLEndTran

SQLRETURN SQLEndTran(   
     SQLSMALLINT HandleType, 
     SQLHANDLE Handle, 
     SQLSMALLINT CompletionType); 

HandleType
SQL_HANDLE_ENV oder SQL_HANDLE_DBC.
Handle
Das Handle (Env oder DBC)
CompletionType
Je nach Absicht:
SQL_COMMIT um die Trasaktion abzuschliessen und durchzuführen.
SQL_ROLLBACK um auf den Zustand der letzten Transaktion zurückzugehen.

Bemerkung

Löst in der Version 3 die Funktion SQLTransact ab.

Das Auslesen: SQLBindCol und SQLFetch

Das Auslesen der Daten geschieht in drei Schritten. Zunächst wird der SQL-SELECT-Befehl abgesetzt, um die Ergebnismenge zu beschreiben. Dies kann beispielsweise mit SQLExecDirect erfolgen.

Dann wird festgelegt, welche Spalten interessieren. Es wird bei dieser Gelegenheit angegeben, in welchen Speicher die Daten gelangen sollen. Zu diesem Zweck wird SQLBindCol verwendet. Es bindet eine Spalte an einen Datenpuffer.

Schliesslich wird Zeile für Zeile mit dem Befehl SQLFetch die Ergebnismenge des SQL-Befehls in den Datenspeicher geholt.

SQLRETURN SQLBindCol(   
     SQLHSTMT StatementHandle, 
     SQLUSMALLINT ColumnNumber, 
     SQLSMALLINT TargetType, 
     SQLPOINTER TargetValuePtr, 
     SQLINTEGER BufferLength, 
     SQLINTEGER * StrLen_or_IndPtr); 

StatementHandle
Das Statement Handle
ColumnNumber
Die Spaltennummer beginnt bei 1. Nur wenn Bookmarks verwendet werden, was durch SQL_ATTR_USE_BOOKMARKS angegeben wird, gibt es eine Spalte 0, die die Bookmarkspalte darstellt.
TargetType
Der Datentyp des Puffers, in den geladen werden soll. Die Angabe der entsprechenden Konstanten führt zu einer Umwandlung der Datenbankinhalte in den angegeben Typ.
TargetValuePtr
Zeiger auf den Datenpuffer, in dem die Daten erwartet werden. Ist dieser Wert 0, wird der Spaltenpuffer von der Spalte gelöst und nicht mehr verwendet.
BufferLength
Grösse des Puffers in Bytes.
StrLen_or_IndPtr
gibt die Länge der gelieferten Daten an oder:
SQL_NO_TOTAL
SQL_NULL_DATA

Liefert als Rügabewert SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, oder SQL_INVALID_HANDLE.

HENV hEnv; // das Handle des Environment
HDBC hDBC; // das Handle der Datenbank
HSTMT hStmt; // das Handle fuer die Anweisung
RETCODE retCode;
char strPerformer[1024];
char strTitle[1024];
long len;
...
retCode = SQLAllocHandle(SQL_HANDLE_STMT, hDBC, &hStmt);
if (retCode==SQL_SUCCESS || retCode==SQL_SUCCESS_WITH_INFO) {
retCode = SQLExecDirect(hStmt, (UCHAR*)"SELECT Performer,Titel FROM Song", SQL_NTS);
if (retCode==SQL_SUCCESS || retCode==SQL_SUCCESS_WITH_INFO) {
SQLBindCol(hStmt, 1, SQL_C_CHAR, strPerformer, 1024, &len);
SQLBindCol(hStmt, 2, SQL_C_CHAR, strTitle, 1024, &len);
while (1) {
retCode = SQLFetch(hStmt);
if (retCode==SQL_SUCCESS || retCode==SQL_SUCCESS_WITH_INFO) {
MessageBox(hWnd, strTitle, strPerformer, MB_OK);
}else {
break;
}
}
} else {
MessageBox(hWnd,"ExecDirect failed", "bad", MB_OK);
}
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}else{
MessageBox(hWnd,"Statement failed", "bad", MB_OK);
}

ODBC Programmierung unter MFC

Die MFC (Microsoft Foundation Class) kapselt die ODBC (Open Database Connectivity) in den Klassen CDatabase und CRecordset.

CDatabase

Öffnen und Schliessen

Nach der Erzeugung per Konstruktor, wird die Verbindung zur ODBC-Schnittstelle mit den Memberfunctions Open oder OpenEx hergestellt.

virtual BOOL Open( LPCTSTR strDataSourceName,
  BOOL bExclusive = FALSE,
  BOOL bReadOnly = FALSE,
  LPCTSTR lpszConnectString = "ODBC;",
  BOOL bUseCursorLib = TRUE );

strDataSourceName ist der Name, der für die Datenbank im ODBC genannt ist. Ist er NULL, wird er im ConnectString angegeben. Der ConnectString enthält DataSource, Username und Kennwort durch Semikolon getrennt z. B. , "DSN=MeineDatenbank;UID=Arnold;PWD=sagichnicht". Wird statt dieses Parameters 0 übergeben, erscheint ein Verbindungsdialog.

Alternativ (und empfohlen) ist der Aufruf OpenEx

virtual BOOL OpenEx( LPCTSTR lpszConnectString, DWORD dwOptions = 0 );

Der ConnectString enthält DataSource, Username und Kennwort durch Semikolon getrennt z. B. , "DSN=MeineDatenbank;UID=Arnold;PWD=sagichnicht". Wird statt dieses Parameters 0 übergeen, erscheint ein Verbindungsdialog.

Der Parameter Optionen kann die folgenden Werte (auch geodert) annehmen:

CDatabase::openExclusive nicht unterstützt
CDatabase::openReadOnly nur in der Datenbank lesen
CDatabase::useCursorLib Lade die ODBC Cursor Library DLL zur Verhinderung von DynaSets
CDatabase::noOdbcDialog zeige keinesfalls den Verbindungsdialog, auch wenn noch Informationen fehlen
CDatabase::forceOdbcDialog zeige immer den ODBC-Verbindungsdialog

Natürlich gibt es auch einen Close-Aufruf. Er hat keine Parameter und löst einen Rollback auf alle laufenden Aktionen und Transaktionen aus. Es empfielt sich, vor Schliessen der Database erst alle Recordsets zu schliessen.

Transaktionen

Mit der Funktion BOOL CDatabase::CanTransact() stellt man fest, ob der ODBC-Treiber überhaupt Transaktionen handhaben kann. Mit dem Aufruf von BOOL CDatabase::BeginTrans(); wird die Transaktion eingeleitet. Es empfielt sich, vor dem ersten Zugriff auf den Recordset BeginTrans aufzurufen. Die Transaktion wird abgeschlossen mit BOOL CDatabase::CommitTrans(); oder zurückgesetzt mit BOOL CDatabase::Rollback();.

Mit dem Aufruf void CDatabase::ExecuteSQL( LPCSTR lpszSQL ); können Datenbankkommandos abgesetzt werden, die keine Ergebnisse in Recordsets zurückliefern.

CRecordset

Der Recordset ist in zwei Ausprägungen vorhanden, dem Dynaset und Schnappschuss. Der Schnappschuss gibt den einmaligen, aktuellen Zustand der Datenbank wieder. Der Dynaset, der nicht von allen Treibern unterstützt wird, synchronisiert die Daten, die von anderen Anwendern verändert werden.

Typischerweise wird ein applikationseigener Recordset von CRecordset abgeleitet.

Erzeugen und Öffnen eines Recordsets

Der Kontruktor benötigt die Adresse der CDatabase-Variablen.

CRecordset::CRecordset( CDatabase* pDatabase = NULL);

Durch Open wird der Recordset mit einem SQL-Statement verbunden.

virtual BOOL Open(
   UINT nTyp = AFX_DB_USE_DEFAULT_TYPE,
   LPCTSTR strSQL = NULL,
   DWORD dwOptions = none );

Der Typ kann folgende Werte annehmen:

AFX_DB_USE_DEFAULT_TYPE
Der Defaultwert des Treibers
CRecordset::dynaset
Die Auswahl und Reihenfolge der Sätze erfolgt bei Eröffnung. Änderungen anderer Anwender werden bei jeder fetch-Operation sichtbar.
CRecordset::snapshot (default)
Die Auswahl und Reihenfolge der Sätze erfolgt bei Eröffnung. Änderungen anderer Anwender werden nur durch Schliessen und wieder Öffnen sichtbar.
CRecordset::dynamic
Die Auswahl und Reihenfolge der Sätze so wie auch Änderungen anderer Anwender werden bei jeder fetch-Operation sichtbar. Viele ODBC-Treiber unterstützen dies nicht.
CRecordset::forwardOnly
Ein Nur-Lese Recordset, der auch nur vorwärts lesen kann.

strSQL ist der Name einer Tabelle, ein SELECT-Statement (mit WHERE-Klausel) oder ein CALL-Statement für eine Stored-Procedure.

dwOptions kann eine oder mehrere der folgenden sein:

CRecordset::none
Der Recordset kann mit Edit, Delete und AddNew verändert werden.
CRecordset::appendOnly
Der Recordset darf nur durch AddNew und nicht per Edit oder Delete verändert werden.
CRecordset::readOnly
Recordset nur zum Lesen öffnen.
CRecordset::optimizeBulkAdd
Optimiert das Hinzufügen mehrerer Sätze bei Verwendung eines vorbereiteten SQL-Statement.
CRecordset::useMultiRowFetch
Damit können mehrere Zeilen in einer einzigen Fetch-Operation geladen werden.
CRecordset::skipDeletedRecords
Überspringt gelöschte Sätze, wenn durch den Recordset navigiert wird. Die Geschwindigkeit wird in bestimmten Fällen reduziert.
CRecordset::useBookmarks
Es werden Bookmarks verwendet, wenn der Treiber dies unterstützt.
CRecordset::noDirtyFieldCheck
Schaltet das automatische Dirty Field Checking (double buffering)aus.
CRecordset::executeDirect
Verwendet keine vorbereiteten SQL-Statements.
CRecordset::useExtendedFetch
Implementiert SQLExtendedFetch statt SQLFetch.
CRecordset::userAllocMultiRowBuffers
Der Anwender wird den Speicher für die Daten alloziieren.

Update eines Recordset

Zunächst wird mit den Befehlen AddNew bzw Edit die Änderung eingeleitet. Die Felder werden besetzt und anschliessend mit Update bestätigt.

AddNew()
AddNew erzeugt einen neuen, leeren Satz und verwendet dazu die Felder des Recordsets. Die Datenfelder werden belegt und per Update in die Datenquelle geschrieben.
Edit()
Mit Edit werden Änderungen im aktuellen Satz eingeleitet. Die Recordset-Variablen werden direkt geändert. Durch den Aufruf von Update gehen die &Aunderungen an die Datenquelle.
Delete()
Löscht den aktuellen Datensatz.

Navigieren

void MoveFirst( );
Durch diesen Aufruf wird der erste Satz des Recordset der aktuelle Satz. Es ist nicht erforderlich MoveFirst direkt nach dem Öffnen des Recordset zu rufen.
void MoveLast( );
Sofern nicht bulk-fetching gesetzt ist, wird der letzte Satz im Recordset der aktuelle.
void MoveNext( );
wechsele den aktuellen Satz zum nächsten im Recordset. Gelöschte Sätze werden nicht unbedingt übersprungen. Die IsDeleted()-Funktion sollte benutzt werden.
void MovePrev( );
wechsele den aktuellen Satz zum vorherigen im Recordset.

Zugriff auf die Felder


void GetFieldValue( LPCTSTR lpszName, CDBVariant& varValue, short nFieldType = DEFAULT_FIELD_TYPE );
void GetFieldValue( short nIndex, CDBVariant& varValue, short nFieldType = DEFAULT_FIELD_TYPE );
void GetFieldValue( LPCTSTR lpszName, CString& strValue );
void GetFieldValue( short nIndex, CString& strValue );

Parameter

lpszName
Feldname
varValue
Eine Referenz auf ein CDBVariant-Objekt, das den Feldwert hält
nFieldType
Der ODBC C-Datentyp des Feldes. Bei Angabe von DEFAULT_FIELD_TYPE wird der C-Typ aus dem SQL Datentyp nach der folgenden Tabelle ermittelt.
nIndex
Der Feldindex (nullbasierend)
strValue
Referenz auf ein CString, das den Feldinhalt als String enthält, ganz gleich, welchen Datentyp das Feld hat.
Beispiel:

   CDatabase db;
   db.OpenEx( NULL, CDatabase::forceOdbcDialog );

   CRecordset rs( &db );
   rs.Open( CRecordset::forwardOnly, _T( "SELECT * FROM MeineTabelle" ) );

   CDBVariant Wert;

   int n = rs.GetODBCFieldCount( );
   while( !rs.IsEOF() ) {
      for( int i = 0; i < n; i++ ) {
         rs.GetFieldValue( i, Wert );
	 . . . 
      }
      rs.MoveNext( );
   }
   rs.Close( );
   db.Close( );

Informationen zu den Feldern


void CRecordset::GetODBCFieldInfo( LPCTSTR lpszName, CODBCFieldInfo& fieldinfo );
void CRecordset::GetODBCFieldInfo( short nIndex, CODBCFieldInfo& fieldinfo );

lpszName
Der Feldname
fieldinfo
Ein Zeiger auf eine CODBCFieldInfo Struktur
nIndex
Feldindex (nullbasierend)
Die Informationen über die Felder findet sich in der Struktur CODBCFieldInfo

Die CODBCFieldInfo Struktur

struct CODBCFieldInfo {
   CString m_strName;
   SWORD m_nSQLType;
   UDWORD m_nPrecision;
   SWORD m_nScale;
   SWORD m_nNullability;
};
m_strName
Feldname
m_nSQLType
Der SQL-Datentyp des Feldes.
m_nPrecision
Die maximale Genauigkeit des Feldes.
m_nScale
Die Grösse des Feldes.
m_nNullability
Gibt an, ob das Feld Nullwerte akzeptiert (SQL_NULLABLE) oder nicht (SQL_NO_NULLS).

Ableitung der Klasse CRecordset Das folgende ist eine (gekürzte) Deklaration, die der AppWizard erzeugt, wenn er auf einer bestehenden ODBC-Datenbank aufsetzt.

class COdbcSet : public CRecordset
{
public:
COdbcSet(CDatabase* pDatabase = NULL);

// Field/Param Data
//{{AFX_FIELD(COdbcSet, CRecordset)
CString m_RefNr;
CTime m_ChangeTimeStamp;
long m_PosCnt;
CString m_AManufacturer;
CString m_AType;
CString m_ASerNr;
//}}AFX_FIELD

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(COdbcSet)
public:
virtual CString GetDefaultConnect(); // Default connection string
virtual CString GetDefaultSQL(); // default SQL for Recordset
virtual void DoFieldExchange(CFieldExchange* pFX); // RFX support
//}}AFX_VIRTUAL
};


Die abgeleitete Klasse enthält für jedes Tabellenfeld eine Variable, über die auf die Felder zugegriffen werden kann. Die Funktion DoFieldExchange, die ebenfalls vom AppWizard ereugt wird, sorgt für den Abgleich zwischen Datenbank und Speicher (beispielsweise bei Update()).



dreamer
Expert
Beitrag vom:
26-12-2003, 13:33:52

Wer mehr über SQL wissen möchte, kann ich die englischsprachige Seite www.sqlcourse.com empfehlen.

-----------------------------------------------------


[back to top]



Userdaten
User nicht eingeloggt

Gesamtranking
Werbung
Datenbankstand
Autoren:04511
Artikel:00815
Glossar:04116
News:13565
Userbeiträge:16552
Queueeinträge:06248
News Umfrage
Ihre Anforderungen an ein Online-Zeiterfassungs-Produkt?
Mobile Nutzung möglich (Ipone, Android)
Externe API Schnittstelle/Plugins dritter
Zeiterfassung meiner Mitarbeiter
Exportieren in CSV/XLS
Siehe Kommentar



[Results] | [Archiv] Votes: 1158
Comments: 0