Session D-STON

Von DBF-Tabellen zu Views - ein harter Weg

Sebastian Flucke
ASCI CONSULTING GmbH


Vorbemerkung

Der Übergang von der direkten Nutzung von FoxPro-DBF-Tabellen zum Einsatz von Views ist ein Schritt, der aus verschiedenen Gründen vollzogen wird oder vollzogen werden sollte.

Die Session D-STON diskutiert alle wesentlichen Aspekte des Übergangs zur Arbeit mit Views und zeigt insbesondere die verborgenen Fallen und Widernisse auf.

Für diese Session werden Grundkenntnisse der Datenbank-Modellierung sowie Kenntnisse in der Arbeit mit VFP-Tabellen vorausgesetzt!

Möglichkeiten des Datenhandling in VFP

Entstehen bei der Programmierung mit Visual FoxPro im Laufe des Verarbeitungsprozesses Daten, die in der weiteren Verarbeitung benötigt werden, so kann man diese Daten auf folgende Arten speichern und wieder benutzen:

  1. Ablegen solcher Daten in Speichervariablen, Arrays oder auch in Properties von Objekten

TIP: Arrays können auch Properties von Objekten sein!
Zum Erstellen von Array-Properties geben Sie beim Anlegen des Properties hinter dem Property-Namen einfach noch die vorläufige Dimensionierung des Arrays an (z.B. MyArray[2,4]).

  1. Ablegen von Daten in temporären Dateien, die man besten mit CREATE CURSOR bzw. mit SELECT-SQL erzeugt.

TIP: Mit SELECT-SQL erzeugte temporäre Dateien sind zunächst immer schreibgeschützt. Mit dem folgenden Mechanismus kann man diese Dateien aber ebenfalls beschreibbar machen:

        SELECT * FROM customer INTO CURSOR myTemp1 NOFILTER
        SELECT 0
        USE ( DBF( "myTemp1" ) ) AGAIN ALIAS myTemp
        USE IN myTemp1

  1. Selbstverständlich können Daten mit temporärem Charakter auch mit den Mechanismen gespeichert werden, die für die dauerhafte Speicherung genutzt werden (siehe dort).

Umgang mit Daten, die dauerhaft gespeichert werden

Visual FoxPro kann auf vier Arten von dauerhaft gespeicherten Daten zugreifen:

  1. auf Daten in DBF-Dateien unter Nutzung der in VFP integrierten Mechanismen der Datenbank- und SQL-Engine
  2. auf alle Datenquellen, die per ODBC erreichbar sind (unter Nutzung von Remote-Views und SQL-Pass-Through)
  3. auf beliebige in Dateien gespeicherte Daten durch Anwendung der LLFFs (Low Level File Functions, also FCREATE(), FOPEN(), FREAD(), FWRITE() usw.)
  4. auf beliebige Daten in speziellen Formaten, sofern für den Zugriff eine passende und in VFP integrierbare DLL oder ein ActiveX-Control existiert

Die Punkte 3. und 4. sind Spezialaufgaben vorbehalten und sollen nicht weiter berücksichtigt werden.

Demgegenüber werden die unterschiedlichen Varianten des Datenzugriffs entsprechend Punkt 1 und Punkt 2 Gegenstand der weiteren Betrachtungen sein.

Arbeit mit DBF-Dateien

Arbeiten mit DBF-Dateien heißt im wesentlichen, diejenigen Visual-FoxPro-Befehle anzuwenden, die ihre historischen Wurzeln im xBase-Standard haben.

Die wesentlichen Befehle aus dieser Kategorie sind:

Ein derartiger Umgang mit DBF-Dateien wird als "datensatz"-orientierte Arbeitsweise bezeichnet. Bei dieser Arbeitsweise kann man je nach Charakter des betreffenden Befehls bestimmte Aktionen an einem oder ggf. auch an mehreren Datensätzen vornehmen (z.B. DELETE ALL).

Eine andere Herangehensweise ist dagegen die "mengen"-orientierte Arbeitsweise, wie sie z.B. bei der Structured Query Language SQL Anwendung findet.

Erste Ansätze von SQL sind schon in den DOS-Versionen von FoxPro (z.B. FoxPro 2.0) zu finden, so z.B. der SELECT-SQL-Befehl sowie die darauf basierende Query By Example-Technologie QBE.

Vor der ersten Version der neuen FoxPro-Generation "Visual FoxPro" war die mengenorientierte Arbeit allerdings der reinen Datenselektion vorbehalten - es gab keinen direkten Weg, auf dem Veränderungen an den selektierten Daten in die eigentlichen Datenquellen zurückgeschrieben werden konnten.

Die Plazierung von Views durch Microsoft

Seit der ersten Version von Visual FoxPro (der Version 3.0) existiert eine neue Form des Lese-Schreib-Zugriffs auf Daten, der sogenannte "View", mit dem sowohl auf Daten in DBF-Tabellen als auch auf Daten in ODBC-Datenquellen zugegriffen werden kann.

Microsoft plaziert die Views innerhalb von VFP als einfach zu nutzende Alternative zu DBF-Dateien. Dieser Anschein wird zusätzlich unterstützt durch die Tatsache, daß Views im Prinzip mit den gleichen Befehlen genutzt werden können wie DBF-Dateien ( also USE, APPEND, SKIP, LOCATE usw.).

Microsoft schreibt dazu in der Hilfe von Visual FoxPro:

Zum Zugriff auf ODBC-Datenquellen unter dem Thema "Designing Client/Server Applications" kann man lesen:

Mit diesen Aussagen wird der Eindruck hervorgerufen, man könne seine Anwendungen ohne Änderungen wahlweise sowohl auf DBF-Dateien als auch alternativ auf Local Views oder auf Remote Views basieren lassen.

Um es vorwegzunehmen - dieser Eindruck ist falsch!

Die vorliegende Session wird sich mit den allgemeinen Problemen beim Übergang von der Arbeit mit DBF-Tabellen zur Arbeit mit Views befassen.

Informationen über spezifische Probleme beim Umstieg auf ODBC-Datenquellen und Remote Views können den Sessions der Sektion C/S entnommen werden.

Die Wahrheit über Views &tt;s>

Was ist ein "View"?

Views sind mit folgenden Wesensmerkmalen ausgestattet:

Ein großer Vorteil von Views ist die Tatsache, daß man sich nicht explizit um die dem Datenmodell entsprechende Synchronisation der Satzpositionen der beteiligten Tabellen zu kümmern braucht (im Gegensatz zu dem in dieser Hinsicht technisch aufwendigeren Umgang mit Relationen und Filtern).

Datensatz-orientiert vs. mengen- bzw. Id-orientiert

Im Zusammenhang mit Views wird häufig davon gesprochen, daß dort im Gegensatz zum direkten Umgang mit DBF-Tabellen nicht mehr "datensatz-orientiert" gearbeitet wird..

Hinter dieser Problematik verbergen sich zwei Aspekte:

Bezüglich der Definition der Ergebnismenge von Views liegt der mengenorientierte Aspekt sozusagen auf der Hand - durch die Definition per SQL-Statement.

SQL-Statements stellen mengenorientierte Definitionen dar:

Dieses Statement bedeutet: Gib mir alle Datensätze der Tabelle "Customer", deren Order-Amount über 10.000 liegt.

Auch komplexere Beispiele lassen die mengenorientierte Arbeitsweise gut erkennen:

Dieses Statement meint: gib mir alle Customer-Daten derjenigen Kunden, die in der Tabelle der gesperrten Kunden eingetragen sind.

 

TIP: Weiterführende Informationen zum Thema SQL finden Sie in der Session D-PRAX!

Wesentlich versteckter ist der Unterschied zur "datensatz-orientierten" Arbeitsweise beim Zurückschreiben von Daten-Änderungen. Hierzu muß man sich folgender Problematik bewußt sein:

TIP: Views arbeiten zum Auffinden von Datensätzen beim Zurückschreiben von Daten-Änderungen nicht mit Datensatznummern!

TIP: Eine Definition für einen "updatable" View muß die Felder mit einbeziehen, die eine eindeutige Identifikation jedes Datensatzes der Ergebnismenge ermöglichen!
Außerdem müssen diese Felder bei der View-Definition als KeyFields gekennzeichnet werden
(siehe unten).

Aus diesen Tatsachen ergeben sich auch Konsequenzen bezüglich der Datenmodellierung (siehe weiter unten).

Das Erstellen von Views

Views werden immer in einem VFP-Datenbank-Container gespeichert.

 

TIP: Es kann durchaus sinnvoll sein, für Views einen oder mehrere Datenbankcontainer anzulegen, die von dem oder den Datenbankcontainern getrennt sind, in denen die DBF-Tabellen verwaltet werden.

Zum Erzeugen von Views existieren zwei grundsätzliche Wege:

  1. Benutzung des View-Designers
  2. Erzeugen des Views mit Programm-Befehlen

Der View-Designer

Der View-Designer ist das in Visual FoxPro integrierte Tool zur Definition von Views:

Aus den Angaben auf den Pages "Fields", "Join", "Filter", "Order By" und "Group By" wird das SQL-Statement erstellt, mit welchem die aktuelle Datensatzmenge des Views bestimmt wird. Dieses SQL-Statement kann nicht direkt editiert werden.

Besondere Aufmerksamkeit verdient die Page "Filter", da man hier die Parametrisierung des Views vornehmen kann:

 

Das Geheimnis der Parametrisierung liegt in der Benutzung des "?" in der Spalte "Example". Dieses Fragezeichen führt dazu, daß bei jedem REQUERY eines Views der Inhalt des dem Fragezeichen folgenden Ausdrucks erneut ausgewertet und in die WHERE-Klausel mit einbezogen wird (verwendet man das Fragezeichen dagegen nicht, wird dieser Ausdruck nur einmalig ausgewertet und Änderungen in dem Ausdruck bei späteren REQUERY-Befehlen nicht mehr berücksichtigt).

 

TIP: Die ?-Technik können Sie auch bei ganz normalen SQL-Befehlen anwenden!


TIP: Auch in anderen Klauseln als der WHERE-Klausel ist die Verwendung der ?-Technik in Views und SQL-Statements syntaktisch zulässig, führt allerdings oft zu unerwarteten oder unbrauchbaren Ergebnissen.

Die Pages "Update Criteria" und "Miscellaneous" enthalten weitere z.T. sehr wichtige Einstellungen, die zusätzlich zu dem SQL-Statement im Datenbankcontainer abgelegt werden (siehe weiter unten)!

Bei der Benutzung des View-Designers ist zu beachten, daß dieser nicht alle Möglichkeiten ausschöpfen kann, die Visual FoxPro bzgl. Views bietet (z.B. wenn man das SQL-Statement eines Views explizit selbst formulieren würde).

Außerdem hat der View-Designer erhebliche Schwächen im Umgang mit JOIN-Verknüpfungen. Das kann im Extremfall dazu führen, daß mit dem View-Designer erzeugte Views mit eben diesem View-Designer nicht mehr geändert werden können (was z.T. von absonderlichen Fehlermeldungen begleitet wird).

Das programmatische Erstellen von Views

Das programmatische Erstellen von Views erfolgt durch den Befehl CREATE SQL VIEW sowie durch eine Menge von ergänzenden DBSETPROP()-Befehlen.

TIP: Der Befehl CREATE SQL VIEW allein ist für die Erstellung von "updatable" Views nicht ausreichend - die Update-Eigenschaften müssen mit DBSETPROP()-Befehlen eingestellt werden!

Die folgenden Quelltextzeilen beinhalten die Definition des Views, der schon im Punkt "View-Designer" als Beispiel gedient hat:

CREATE SQL VIEW "VCUSTOMER" ;

   AS SELECT Customer.cust_id, Customer.company FROM testdata!customer

 

DBSetProp('VCUSTOMER', 'View', 'UpdateType', 1)

DBSetProp('VCUSTOMER', 'View', 'WhereType', 3)

DBSetProp('VCUSTOMER', 'View', 'FetchMemo', .T.)

DBSetProp('VCUSTOMER', 'View', 'SendUpdates', .T.)

DBSetProp('VCUSTOMER', 'View', 'UseMemoSize', 255)

DBSetProp('VCUSTOMER', 'View', 'FetchSize', 100)

DBSetProp('VCUSTOMER', 'View', 'MaxRecords', -1)

DBSetProp('VCUSTOMER', 'View', 'Tables', 'testdata!customer')

DBSetProp('VCUSTOMER', 'View', 'Prepared', .F.)

DBSetProp('VCUSTOMER', 'View', 'CompareMemo', .T.)

DBSetProp('VCUSTOMER', 'View', 'FetchAsNeeded', .F.)

DBSetProp('VCUSTOMER', 'View', 'FetchSize', 100)

DBSetProp('VCUSTOMER', 'View', 'Comment', "")

DBSetProp('VCUSTOMER', 'View', 'BatchUpdateCount', 1)

DBSetProp('VCUSTOMER', 'View', 'ShareConnection', .F.)

 

*!* Field Level Properties for VCUSTOMER

* Props for the VCUSTOMER.cust_id field.

DBSetProp('VCUSTOMER.cust_id', 'Field', 'KeyField', .T.)

DBSetProp('VCUSTOMER.cust_id', 'Field', 'Updatable', .T.)

DBSetProp('VCUSTOMER.cust_id', 'Field', 'UpdateName', ;

                  'testdata!customer.cust_id')

DBSetProp('VCUSTOMER.cust_id', 'Field', 'DataType', "C(6)")

* Props for the VCUSTOMER.company field.

DBSetProp('VCUSTOMER.company', 'Field', 'KeyField', .F.)

DBSetProp('VCUSTOMER.company', 'Field', 'Updatable', .T.)

DBSetProp('VCUSTOMER.company', 'Field', 'UpdateName', ;

                  'testdata!customer.company')

DBSetProp('VCUSTOMER.company', 'Field', 'DataType', "C(40)")

Diese Variante hat gegenüber dem View-Designer den Vorteil der freien Definierbarkeit des SQL-Statements, was allerdings mit dem Nachteil der aufwendigeren Codierung der DBSETPROP-Sequenzen verbunden ist.

 

TIP: Benutzen Sie das Datenbank-Modellierungstools xCase, welches Ihnen die dialogorientierte Definition von Views erlaubt, ohne auf die volle Leistungsfähigkeit von Visual-FoxPro-Views verzichten zu müssen (siehe Session V-XCAS)!

Das Erstellen von "updatable" Views

Für "updatable" Views sind bei der Definition einige zusätzliche Angaben notwendig, die im View-Designer auf der Page "Update Criteria" zu finden sind:

 

 

Die grundlegende Voraussetzung ist das Definieren derjenigen Felder, deren ggf. veränderte nhalte zurückgeschrieben werden sollen.
Ein Beispiel für die analoge programmatische Einstellung dieser Eigenschaft  ist der Befehl
DBSetProp('VCUSTOMER.company', 'Field', 'Updatable', .T.).

Vorher muß man jedoch je beteiligter Tabelle die sogenannten KeyFields bestimmen, also diejenigen Felder, anhand deren Inhalte der jeweilige Datensatz zum Zurückschreiben veränderter Werte wieder aufgefunden wird.
Ein Beispiel für die analoge programmatische Einstellung dieser Eigenschaft  ist der Befehl
DBSetProp('VCUSTOMER.cust_id', 'Field', 'KeyField', .T.).

Die beiden vorherigen Einstellungen bleiben allerdings wirkungslos, solange die Option "Send SQL updates" nicht aktiviert ist!
Ein Beispiel für die analoge programmatische Einstellung dieser Eigenschaft  ist der Befehl
DBSetProp('VCUSTOMER', 'View', 'SendUpdates', .T.).

 

TIP: Wenn mit einem "updatable" View auch Datensätze angehängt werden sollen, dann müssen auch die KeyFields als "updatable" gekennzeichnet werden!

TIP: Versehen Sie in Ihrem Datenmodell alle Tabellen mit einem eindeutigen KeyField, dann ist die Einstellung der Option "Key fields only" ausreichend.

Tips und Tricks

Definieren von Views

Arbeiten mit Joins

Joins sind eine Möglichkeit, normalisierte Tabellen miteinander zu verknüpfen. Das folgende Beispiel verknüpft Rechnungsdaten mit Kundendaten und liefert eine Liste aller Kunden, die Bestellungen abgegeben haben, bei denen Einzelpositionen enthalten sind, deren Bestellmenge > 100 ist:

SELECT DISTINCT Customer.cust_id, Customer.company;

   FROM  testdata!orditems ;

   INNER JOIN testdata!orders;

      ON  Orders.order_id = Orditems.order_id;

   INNER JOIN testdata!customer ;

      ON  Customer.cust_id = Orders.cust_id ;

   WHERE Orditems.quantity > 100

Besonders interessant ist die Verwendung sogenannter Outer Joins. Bei diesen Joins werden auch Datensätze in die Ergebnismenge einbezogen, die bezüglich der beteiligten Tabellen nicht vollständig sind:

SELECT Customer.cust_Id, Customer.company, Phone.number;

   FROM  customer ;

   LEFT JOIN phone ;

      ON Customer.cust_Id = Phone.cust_Id

Das Ergebnis des vorstehenden SQL-Statements beinhaltet alle Kunden und deren Telefonnummern, Kunden ohne Einträge in der Tabelle "phone" werden aber auch mit aufgeführt (was bei einem INNER JOIN nicht der Fall wäre)!

Bei der Verwendung von Joins ist generell zu beachten, daß die maximale Anzahl der Joins, die Visual FoxPro innerhalb eines Views verarbeiten kann, bei zwischen 7 und 9 Joins liegt. Diese Begrenzung kann bei einem stark normalisierten Datenmodell durchaus zu Problemen führen.

Zu den verschiedenen Arten von Joins und den dabei zu beachtenden Besonderheiten siehe Session D-PRAX!

Datentypen und Feldlängen

Aus der Tatsache, daß bei View-Definitionen SQL-Statements verwendet werden, ergeben sich bestimmte Eigenheiten bzgl. der Feld-Definitionen der Felder des Views (siehe auch Session D-PRAX).

Bei den Felddefinitionen der Ergebnisfelder ist prinzipiell zu unterscheiden zwischen direkt übernommenen Feldern einerseits und sogenannten Expressions andererseits:

SELECT cust_Id, UPPER( company) AS UCompany FROM customer

Im vorstehenden Beispiel ist das Feld "cust_Id" ein direkt übernommenes Feld, "UCompany" dagegen eine Expression.

Der Datentyp und die Feldlänge eines direkt übernommenen Feldes ergibt sich prinzipiell immer aus dem Datentyp und der Feldlänge des Originalfeldes!

 

ACHTUNG! Andere Eigenschaften außer dem Datentyp und der Feldlänge werden nicht automatisch übernommen (also keine Default-Werte, keine Captions, keine InputMasks usw.). Auch dieses Manko läßt sich durch den Einsatz der Modellierungstools xCase bereinigen
(siehe Session V-XCAS).

Der Datentyp einer Expression ergibt sich aus dem Datentyp des Ergebnisses des verwendeten Ausdrucks im ersten Datensatz.

Dabei ist zu beachten, daß auf diesem Wege keine Felder der Feldtypen "M (Memo)", "I (Integer)", "B (Double)" und "F (Float)" erzeugt werden können!

Besonders spannend wird die Angelegenheit im folgenden Fall:

SELECT IIF( quantity < 5, .NULL., quantity ) AS yQuantity from OrdItems

Ergibt es sich nun durch die konkrete Datenkonstellation, daß der Inhalt im ersten Datensatz &tt; 5 ist, dann kann Visual FoxPro keinen Datentyp ermitteln und die ganze Angelegenheit endet mit dem Laufzeitfehler "1890 SQL: cannot determine datatype of SQL Column".

Die Feldlänge einer Expression ergibt sich aus den Parametern des Inhalts der Expression im ersten Datensatz!

Daraus sind zwei Schlußfolgerungen zu ziehen:

  1. Bei Zeichenketten-Ausdrücken sollte immer darauf geachtet werden, daß der Ausdruck ein Ergebnis mit einer festen Länge ergibt (also z.B. kein ALLTRIM() als äußerste Funktion verwenden).
    Gut geeignet zum Herstellen einer festen Länge sind z.B. die Funktionen LEFT(), RIGHT(), SUBSTR(), PADC(), PADR() und PADL().
  2. Bei numerischen Ausdrücken bezieht sich diese Aussage nicht nur auf die Feldlänge, sondern damit auch indirekt auf die Anzahl der Vor- und Nachkommastellen. Hier kann man eine bestimmte Mindest-Menge an Vor- bzw. Nachkommastellen erzwingen, indem man dem numerischen Ausdruck einen Faktor hinzufügt. Der nachfolgende Befehl erzeugt ein Feld xQuantity mit mindestens 10 Vorkommastellen:

SELECT quantity, quantity*0000000001 AS xQuantity from OrdItems

Null-Werte

Auch NULL-Werte bedürfen ggf. einer besonderen Behandlung. In der Ergebnismenge von Views können NULL-Werte auf folgenden Wegen entstehen:

Will man die Entstehung solcher Null-Werte verhindern, kann man mit Expressions unter Zuhilfenahme der Funktion NVL arbeiten (siehe auch Punkt "Datentypen und Feldlängen").

Weitere Begrenzungen

Weitere Begrenzungen ergeben sich aus der Tatsache, daß für Views die gleichen technischen Begrenzungen gelten wie für Tabellen (Anazhl Felder, Datensatzlänge usw.).

Außerdem kann es beim Überschreiten bestimmter nicht fest definierter Grenzen in verschiedenen Bereichen einer View-Definition (SQL-Klauslen) dazu kommen, daß ein View in irgendeiner Hinsicht zu komplex wird, was sich entweder durch die Fehlermeldung "SQL expression is too complex (Error 1845)" oder durch völlig unmotiviertes und undefiniertes Fehlverhalten eines Views äußert.

Einsatz von Views im Programm

Allgemeine Bemerkungen

Die programmatische Benutzung von Views unterscheidet sich bis auf wenige Ausnahmen auf den ersten Blick nicht von der Benutzung von DBF-Tabellen.

Der technische Hintergrund dafür ist die Tatsache, daß die Daten eines Views VFP-intern in einer temporären Tabelle abgelegt werden. Diese Tabelle kann mit den normalen Foxpro-Befehlen genutzt und manipuliert werden.

Damit wird aber auch sofort ein wichtiger Punkt in der Arbeit mit Views deutlich: Datensatzmengen für Views müssen möglichst klein gehalten werden.

Der Grund dafür ist einfach: Beim Öffnen eines Views werden standardmäßig alle Datensätze der Ergebnismenge in diese temporäre Datei kopiert. Was dies bei einem nicht eingeschränkten View auf eine Tabelle mit 1.000.000 Datensätzen sowohl laufzeitseitig als auch plattenplatzseitig bedeutet, kann sich jeder wohl ausmalen.

 

TIP: Die Menge der Datensätze eines Views sollte immer so gering wie möglich gehalten werden!

Das heißt aber andererseits, daß man bei einer view-basierten Eingabe dem Benutzer kein Grid zur Verfügung stellen kann, in dem alle 100.000 existierenden Kunden angezeigt werden, denn dies wäre platz- und laufzeitseitig nicht vertretbar.

Diese Problematik führt zu zwei bei der Arbeit mit Views sehr verbreiteten Verfahrensweisen, der Vorselektion und dem Einzelsatz-View.

Die Vorselektion ist ein Bestandteil der Benutzerführung, die dem Nutzer vor dem Aufblenden einer solchen Übersichtsliste sinnvolle Möglichkeiten gibt, die zu erwartende Menge an darzustellenden Datensätzen möglichst sehr zu reduzieren (z.B. durch die Eingabe von Filterkriterien, ähnlich wie im Suchen-Dialog des Windows-Explorers).

Für derartige Zwecke benötigt man Views, deren Ergebnismenge sich über viele verschiedene View-Parameter einschränken läßt.

Wird auf diesem Wege ein bestimmter Datensatz zur weiteren Verarbeitung ausgewählt, dann kommt für die eigentliche Bearbeitung ein sogenannter Einzelsatz-View zum Einsatz. Dieser View wird nur mit der Id des gesuchten Datensatzes parametrisiert und enthält dann auch nur diesen einen Datensatz, wodurch die Abfrage sehr schnell geht.

Ein solcher Einzelsatz-View ist dann üblicherweise auch ein "updatable" View, mit dem Daten in die Originaltabellen zurückgeschrieben werden können.

SET-Einstellungen

Auf die Ergebnismenge von Views haben folgende SET-Einstellungen Auswirkungen:

Mit der Einstellung von SET ANSI legt man fest, wie Visual FoxPro beim Vergleichen mit unterschiedlich langen Zeichenketten umgehen soll.

Bei SET ANSI ON ergibt ein Vergleich unterschiedlich langer Zeichenketten immer ein .F. ("Tom" = "Tommy" ergibt .F.).

Bei SET ANSI OFF dagegen wird bei unterschiedlich langen Zeichenketten immer dann ein .T. zurückgegeben, wenn die längere Zeichenkette bis zur Länge der kürzeren mit der kürzeren Zeichenkette übereinstimmt ("Tom" = "Tommy" ergibt .F.).

SET DELETED ist ebenfalls ein wichtige Einstellung. Sie entscheidet darüber, ob gelöschte Datensätze in die Ergebnismenge des Views einbezogen werden oder nicht.

Besonders hinterlistig an dieser Problematik ist die Tatsache, daß auch die bei SET DELETED OFF mit einbezogenen gelöschten Datensätze im View selbst aber keine Löschmarkierung haben!

Parametrisierung von Views

Die Parametrisierung von Views erfolgt durch die Verwendung der "?"-Syntax in der WHERE-Klausel des zugrundeliegenden SQL-Statements, z.B.

SELECT * FROM customer WHERE cust_Id = ?cMyCustId

Der Unterschied zur Verwendung der Variablen cMyCustId ohne das "?"-Zeichen liegt in der Möglichkeit, die Variable neu zu belegen und den REQUERY()-Befehl aufzurufen, der den View erneut mit Daten füllt und dabei den neuen Inhalt der Variablen auswertet (was ohne das "?"-Zeichen nicht geschehen würde).

Eine solche Parametrisierung läßt sich übrigens sinnvoll mit der Einstellung SET ANSI OFF kombinieren, denn in diesem Fall werden bei cMyCustId = "Ant" alle Sätze selektiert, die mit "Ant" beginnen, was für Filterungen eine willkommene Erleichterung ist.

Läßt man bei SET ANSI OFF den Viewparameter vollständig leer (cMyCustId = ""), werden alle Datensätze in die Ergebnismenge aufgenommen!

View-Klassen

Problematisch ist allerdings die Tatsache, daß man einen parametrisierten View nur dann per Requery neu füllen kann, wenn die entsprechende Parameter-Variable auch "erreichbar" ist.

Außerdem muß man sichern, daß ein Requery eines Views immer bei identischen Einstellungen von SET ANSI und SET DELETED ausgeführt wird, um eindeutige Ergebnisse zu erhalten.

Aus diesem Grund ist es eine hilfreiche Angelegenheit, wenn man sich eine View-Klasse programmiert.

Diese Klasse enthält die notwendigen Methoden und Properties, um alle View-Aktivitäten mit reproduzierbaren Ergebnissen abwickeln zu können, wie im folgenden fragmentarisch aufgezeigt wird:

**************************************************

DEFINE CLASS view_manager AS label

 

   Caption = "View-Manager"

   *-- .T. = SET ANSI ON, .F. = OFF

   lansi = .F.

   *-- .T. = SET DELETED ON, .F. = OFF

   ldeleted = .T.

   *-- Name des Views

   cname = ""

   *-- Datenbank des Views

   cdatabase = ""

   *-- zu verwendender Arbeitsbereichsname

   calias = ""

   Name = "view_manager"

 

   *-- öffnet den View

   PROCEDURE viewopen

      LOCAL lnSelect

 

      WITH This

 

         lnSelect = SELECT()

 

         USE ( .cDataBase + "!" + cName ) IN 0 ALIAS ( .cAlias ) NODATA

         .ViewRequery()

 

         SELECT ( m.lnSelect )

 

      ENDWITH

   ENDPROC

 

   *-- zieht neue Daten für den View

   PROCEDURE viewrequery

      LOCAL lcSetDeleted, lcSetAnsi, lnSelect

 

      WITH This

 

         lnSelect = SELECT()

         lcSetDeleted = SET( "DELETED" )

         lcSetAnsi = SET( "ANSI" )

 

         SELECT ( .cAlias )

         REQUERY()

 

         SET DELETED &lcSetDeleted.

         SET ANSI &lcSetAnsi.

 

         SELECT ( m.lnSelect )

 

      ENDWITH

   ENDPROC

 

ENDDEFINE

*

*-- EndDefine: view_manager

**************************************************

Hat der zu verarbeitende View nun View-Parameter, dann erstellt man sich ein Subklasse des View-Managers und fügt die View-Parameter dort als Properties hinzu:

**************************************************

DEFINE CLASS my1stview AS view_manager

 

   *-- Viewparameter für die Cust_ID

   vp_ccust_id = ""

   Name = "my1stview"

 

ENDDEFINE

*

*-- EndDefine: my1stview

**************************************************

In der Definition des Views gibt man als View-Parameter nun keine einfache Variable an, sondern bezieht sich auf das erstellte Property:

 

SELECT * FROM customer WHERE cust_Id = ?This.vp_ccust_id

Das bedeutet zwar, daß dieser View dann nur noch innerhalb der zugehörigen View-Klasse arbeitet, aber dafür kann man sicher sein, daß die Ergebnisse nicht von zufälligen Einstellungen abhängig sind!

Locking und Buffering bei Views

Views lassen sich in Visual FoxPro generell nur den mit Buffermodi 3 bzw. 5 benutzen (optimistisches Row- oder Table-Buffering, zu dieser Problematik siehe auch Session D-NETZ).

Damit steht man schlagartig vor der Tatsache, daß man beim Zurückschreiben von Änderungen mit dem Fehler "Update conflict (Error 1585)" rechnen muß.

 

TIP: Hat man sich wie oben beschrieben eine View-Klasse programmiert, dann kann man in dieser Klasse auch die Update-Funktionalität sowie die dazu notwendige Fehlerbehandlung kapseln!

Eine weitere Problematik ergibt sich aus der Tatsache, daß beim Öffnen eines Views die beteiligten DBF-Tabellen ebenfalls geöffnet werden.

Als Arbeitsbereichsnamen für die eigentlichen DBF-Tabellen werden die Originalnamen der Tabellen verwendet (z.B. "Customer").

Wenn die betreffende Quelltabelle allerdings mit eben diesem Alias schon geöffnet ist, dann wird der schon offene Arbeitsbereich genutzt.

So weit - so gut.

Spannend wird diese Angelegenheit dann, wenn für den schon geöffneten Arbeitsbereich ein Buffermode > 1 eingestellt ist. In diesem Fall landen die Änderungen aus dem Arbeitsbereich des Views beim TableUpdate nicht direkt auf den zugrundeliegenden Tabellen, sondern zunächst im Puffer der Tabelle - und erst bei einem dortigen TableUpdate wird dann wirklich in die Tabelle zurückgeschrieben.

Views auf Views

Bei der Definition von Views kann man sich als Datenquelle auch auf existierende Views beziehen, was die Komplexität des einzelnen Views u.U. erheblich reduziert.

Zu beachten ist bei dieser Verfahrensweise lediglich eine gewisse Organisation beim Update und beim Requery dieser Views:

Auch in diesen Fällen kann man sich bei der Verwendung einer View-Klasse gut behelfen und in die entsprechenden Methoden der Klasse die zusätzlichen Aktionen mit eintragen, die immer gekoppelt ablaufen müssen, wie das folgende Beispiel zeigt:

*-- zieht neue Daten für den View

   PROCEDURE viewrequery

 

      WITH This

 

         * Requery des vorgelagerten Views

         .Parent.ViewObject_ViewB.Requery()

 

         * eigentliches Requery()

         DODEFEAULT()

 

      ENDWITH

   ENDPROC

Zurückschreiben von Daten

Eine weitere wichtige Frage für das Zurückschreiben von Daten ist die Wirkung von Validation Rules.

Bei einer gepuffert geöffneten DBF-Tabelle kann man schon im Puffer keine Felder mit Inhalten belegen, die den Field Validation Rules widersprechen. Auch können keine Datensätze im Puffer erzeugt werden, die gegen die Record Validation Rule verstoßen.

Anders dagegen bei einem View: Hier werden die Rules der zugrundeliegenden Tabellen erst abgearbeitet, wenn die Änderungen per TableUpdate zurückgeschrieben werden sollen.

Daraus folgt, daß man bei Views damit rechnen muß, daß beim eigentlichen TableUpdate() einige zusätzliche Fehler auftreten können, die bei der reinen Tabellenarbeit schon vorher abgefangen werden.

ACHTUNG: Diese Aussage gilt selbstverständlich nicht für Validation Rules, die explizit auf Ebene des Views definiert wurden!

Neben den Validation-Rules können beim Updaten auch die Trigger der eigentlichen Tabellen noch verhindern, daß Änderungen wirklich zurückgeschrieben werden können. In diesem Fall erhält man die Fehlermeldung "Trigger failed (Error 1539)" und muß das AERROR()-Array auswerten.

Ein weiterer Problemkreis ist das Zurückschreiben von Daten mit einem View, der auf mehreren Tabellen basiert.

Sind die Quell-Tabellen untereinander über Trigger verbunden und abgesichert, kann es beim TableUpdate() durch VFP-interne Reihenfolgenprobleme zu Schwierigkeiten kommen. Dies ist dann der Fall, wenn die beteiligten Tabellen bzgl. des Datenmodells zueinander in einer Parent-Child-Beziehung stehen, die über "restricted insert" abgesichert ist.

Beim Update eines in diesem View neu angelegten Datensatzes kann es dazu kommen, daß VFP zuerst in die Child-Tabelle zurückschreiben will, was dann von VFP mit einem Trigger-Fehler abgewiesen wird, da ja noch kein Parent-Satz existiert.

Durch diese Problematik sind updatable Views über mehrere Tabellen leider unbrauchbar für das Anlegen neuer Datensätze, wenn zwischen den beteiligten Tabellen im Sinne der Referentiellen Integrität ein "restricted Insert" besteht.

Konsequenzen für die Datenmodellierung

Eine wichtige Schlußfolgerung für die Datenmodellierung ergibt sich aus der Tatsache des ID-orientierten Wiederauffindens von Datensätzen beim Zurückschreiben der Daten:

 

TIP: Versehen Sie jede Tabelle mit einem eindeutigen Schlüsselfeld!

Auch sogenannte Cross-Tables, die oft eigentlich nur zwei Foreign-Keys beinhalten, sollten unbedingt mit einem eigenen Primary-Key versehen werden, da dies den Umgang mit diesen Tabellen insbesondere beim Einsatz von Views erheblich erleichtert.

Zusammenfassung

Der Umstieg von der Arbeit mit reinen DBF-Tabellen auf den Einsatz von Views ist eine lohnenswerte Angelegenheit, für die man jedoch ein angemessenes Maß an Zeitaufwand einplanen muß, um dann die Effektivität der View-Technologie auch erfolgreich nutzen zu können.

Besonders im Hinblick auf die Austauschbarkeit des Datenspeicherungssystems einer Applikation ist der Einsatz von Views eine notwendige Voraussetzung, die allein allerdings nicht ausreichend ist (siehe Sessions der Sektionen TIER und C/S).