Grundlagen Control-ADO

Die aktuelle Implementierung setzt auf ADO Version 2.6 auf und benötigt die Installation der ADO-Komponenten (im WinCC OA Setup enthalten). Unter Linux wird die Qt-Library automatisch beim Setup installiert. Damit ist der Zugriff sowohl auf lokale Datenquellen als auch auf solche im Netzwerk möglich.

Da die ADO- bzw. ODBC-Schnittstelle je nach Implementierung stark variieren kann (je nachdem, welche Methoden tatsächlich implementiert wurden), ist es sinnvoll, vor der Implementierung einer Applikation die Fähigkeiten der Schnittstelle zu der verwendeten Datenquelle zu untersuchen. So sind z.B. die Verwendung von Transaktionen oder auch das parallele Lesen und Ändern von Recordsets sowie das Rückwärtslesen von Recordsets nicht bei jeder Datenquelle verfügbar.

Anmerkung:

Bitte beachten Sie, dass vor Nutzung einer ADO-Funktionalität die entsprechenden Treiber der zu verwendenden Datenbank installiert sein sollten (z.B. MS Excel, MS Access, Oracle Client, etc.). Beachten Sie weiters bei der Nutzung der ADO-Schnittstelle die Lizenz-Bestimmungen in der Datei ADO_EULA.txt.

Anmerkung:

Zugriffe mit mehreren parallelen Verbindungen unter ADO mit MS Access können Transaktionslogik-Probleme haben (d.h. die Isolierung der Transaktionen je Verbindung funktioniert nicht).

"CtrlADO" - DLL

Um die Schnittstellen unter Windows bzw. Linux verwenden zu können, muss über das Schlüsselwort #uses die DLL-Library geladen werden. Siehe Kapitel Laden einer Control-Library. Alternativ kann in der [ctrl]-Sektion der WinCC_OA_Path/config/config.level Datei der folgende Eintrag gesetzt werden:

[ctrl]
CtrlDLL = "CtrlADO"

Falls sie auch im UI verwendet wird, ist der Eintrag auch in der [ui]-Sektion notwendig.

Anmerkung:

Um unter Linux den ODBC Treiber verwenden zu können, muss auf dem Rechner das unixODBC Paket installiert sein. Siehe www.unixodbc.org.

Datentypen

Für die Zugriffsvariablen wurden folgende Datentypen im CTRL implementiert:

  • dbConnection

  • dbCommand

  • dbRecordset

DB-Zugriff

Ein DB-Zugriff läuft grundsätzlich über eine dbConnection ab. Diese spezifiziert die Datenquelle sowie weitere Verbindungsparameter (Benutzername, Passwort etc.). Auf eine dbConnection können in Folge Kommandos (dbCommand-Objekte) abgesetzt werden (i.A. SQL-Befehlssequenzen zur Manipulation der Datenquelle) oder Recordsets geöffnet werden. Auch Transaktionen werden auf dbConnection-Ebene abgehandelt.

Anmerkung:

Stellen Sie sicher, dass Ihre SQL Syntax den Regeln Ihres Servers entspricht! So gilt bei Microsoft Access der Asterisk "*" für den LIKE Operator, SQL-Server hingegen verwenden den ANSI-Standard "%": SELECT * FROM Products WHERE ProductName Like 'M%'. Ein ";" am Ende eines SQL-Statements ("SELECT * FROM x;") kann ebenfalls Probleme machen.

Recordsets

Recordsets definieren eine normalerweise durch eine SQL SELECT-Anweisung festgelegte tabellarische Datenuntermenge der Datenquelle. Jedes Recordset hat eine aktuelle Zeile (außer es ist leer). Nach dem Öffnen des Recordsets ist dies die erste von der DB gelieferte Zeile. In einem Recordset kann navigiert werden, (erste Zeile, nächste, vorige, letzte usw.), d.h. die Position der aktuellen Zeile kann geändert werden und die Dateninhalte der Spalten der aktuellen Zeile können durch Angabe der Spaltennummer gelesen und beschrieben werden. Es können neue Zeilen angelegt, Zeilen verändert und auch gelöscht werden.

Mögliche Datentypen

Als Datentyp zum Lesen und Schreiben von Spalteninhalten (Feldern) wird zum Teil anyType verwendet, wobei folgende Datentypen umgesetzt werden können:

  • Ganzzahlige Werte (8, 16, 32 und 64 Bit, mit/ohne Vorzeichen)

  • Fließkommazahlen (float, double, long, longLong (64 Bit), ulong und uLongLong (64 Bit))

  • Zeichenketten

  • Datum/Uhrzeit (bis zu einer Genauigkeit in Sekunden)

  • Boolsche Werte

  • Blobs (nur unter Linux)

NULL-Werte der Datenbank (Wert nicht bekannt) werden als Leerstring zurückgegeben. Nicht bekannte Datentypen (dbPutField kann keine DynVars, keine LangTextVars sowie keine BlobVars, RecVars und db...-Datentypen) werden als String "N/A" ("not available") retourniert.

Requery

Die Daten eines Recordsets können intern zwischengespeichert werden. Um die Änderungen an der Datenquelle, welche durch andere Anwender seit dem Öffnen des Recordsets durchgeführt wurden, sichtbar zu machen, ist eine "Requery"-Methode vorhanden.

Transaktionen

Transaktionen erlauben die Manipulation der Daten "am Stück". D.h. alle Änderungen an der Datenquelle seit einem zu definierenden Anfangszeitpunkt (Transaktionsstart) werden intern gepuffert und am Ende durch die Applikation freigegeben ("Commit") oder verworfen ("Rollback"). Die Änderungen sind für andere Anwender der Datenquelle erst nach Transaktionsende sichtbar, für die ändernde Applikation selbst jedoch sofort. Transaktionen schließen Änderungen der Datenquelle über dbCommand- und dbRecordset-Objekte ein.

Fehlerabfrage

Jede DB-Methode (mit Ausnahme der dbEOF-Methode, welche einen boolschen Rückgabewert hat) liefert einen numerischen Rückgabewert, wobei 0 Erfolg signalisiert. Bei von 0 verschiedenen Werten kann mit der dbGetError-Methode der aktuell anstehende interne Fehlercode, ein Fehlertext und ein SQL-Fehlercode abgerufen werden. Unter Windows ist der Rückgabefehlercode ADO spezifisch.

Mit dem folgenden Codestück werden alle Datensätze der Tabelle "PERS" einer Access-Datenbank ausgelesen und die ersten beiden Spalten jeder Zeile angezeigt.

Anmerkung:

Für das Beispiel benötigen Sie eine eine 64-Bit ODBC Access Datenbank-Datenquelle. Fügen Sie Datenbank-Datenquelle hinzu (Startmenü -> ODBC Data Sources 64-Bit -> Create New Data Source -> Microsoft Access Driver ).

#uses "CtrlADO"
main(mapping event)
{
  int rc;
  dbConnection conn;
  dbRecordset rs;
  anytype fld;
  int errCnt, errNo, errNa;
  string errDesc, errState;
  rc = dbOpenConnection("DSN=MyAccessDSN", conn); // use 64bit driver
  if (rc != 0)
  {
    dbGetError(conn, errCnt, errNo, errNa, errDesc, errState);
    DebugN("Error", rc, conn, errCnt, errNo, errNa, errDesc, errState);
  }

  if (!rc)
  {
    rc = dbOpenRecordset (conn, "SELECT * FROM PERS", rs);
    if (!rc)
    {
      while (!rc && !dbEOF (rs))
      {
        rc = dbGetField (rs, 0, fld);
        if (!rc)
          DebugN("Feld 0: " , fld);
        rc = dbGetField (rs, 2, fld);
        if (!rc)
          DebugN("Feld 2: " , fld);
        rc = dbMoveNext (rs);
      }
      dbCloseRecordset (rs);
    }
    else
    {
      dbGetError(conn, errCnt, errNo, errNa, errDesc, errState);
      DebugN("Error", rc, conn, errCnt, errNo, errNa, errDesc, errState);
    }
    dbCloseConnection (conn);
  }
}

Für Problembehandlung bei Control-ADO, siehe Kapitel Problembehandlung Control-ADO.