Base
 

Praktische Beispiele für besondere Problemstellungen in Base

Inhalt

Wozu Datenbankbeispiele?

Zeitmessung in einer Datenbank

Tabellen

Formulare

Das Formular «Einzelmessung»

Makros für das Formular «Einzelmessung»

Das Formular «Startzeit_Gruppe»

Makros für das Formular «Startzeit_Gruppe»

Das Formular «Nur_Start»

Makros für das Formular «Nur_Start»

Das Formular «Nur_Ziel»

Makro für das Formular «Nur_Ziel»

Das Formular «Einzelmessung_Zeitfeld»

Makro für das Formular «Einzelmessung_Zeitfeld»

Berichte

Serienbriefe direkt aus Base heraus

Tabellen

Formulare

Das Formulare «Anschrift»

Ausdruck aus dem Formular «Anschrift»

Makrosteuerung zum Ausdruck im Serienbrief

Das Formular «Anschrift_Textfelder»

Ausdruck aus dem Formular «Anschrift_Textfelder»

Makrosteuerung zum Ausdruck mit Textfeldern

Das Formular «Etiketten_Serienbrief_im_Formular»

Das Formular «Rechnung»

Ausdruck aus dem Formular «Rechnung»

Makrosteuerung zum Ausdruck der Rechnung im Serienbrief

Zusammenführen der Druckdaten in einer Abfrage

Das Formular «Rechnung_Textfelder»

Ausdruck aus dem Formular «Rechnung_Textfelder»

Makrosteuerung zum Ausdruck der Rechnung mit Textfeldern

Das Formular «Rechnung_Textfelder_Uebertrag»

Ausdruck aus dem Formular «Rechnung_Textfelder_Uebertrag»

Makrosteuerung zum Ausdruck der Rechnung mit Übertrag

Das Formular «Rechnung_Textfelder_Uebertrag_Seriendruck»

Makrosteuerung zum Ausdruck der Serien-Rechnung mit Übertrag

Das Formular «Waren»

Bilder in Base einbinden

Tabellen

Formulare

Das Formular «Pfadeingabe»

Das Formular «Pfadeingabe_Tabellenkontrollfeld»

Makrosteuerung für die Formulare mit Pfadspeicherung

Das Formular «Pfadeingabe_Tabellenkontrollfeld_Pfadangabe»

Makrosteuerung für die relative Pfadangabe

Die Formulare «Bildaufnahme» und «Bildaufnahme_Name»

Makrosteuerung für das Formular «Bildaufnahme»

Makrosteuerung für das Formular «Bildaufnahme_Name»

Berichte

Mailversand aus einer Datenbank heraus

Tabellen

Formular

Makrosteuerung für den Mauszeiger über einem Link

Makrosteuerung für den Programmstart mit Link

Makrosteuerung für den Aufruf des Mailprogramms mit Parametern

Suchen und Filtern von Daten ohne Makroeinsatz

Tabellen

Datenzusammenfassung in einer Ansicht

Erstellung einer Filter-Tabelle

Ansichten für eine Standardfilterung mit Subformular

Abfragen

Filter_Form_Start

Filter_Form

Filter_Form_Subform

Filter_Form_Subform_3Filter

Listenfeld_Kategorie_ohne_Eintrag

Parametersuche_Medien

Suche_Form

Suche_Form_Subform

Suche_Filter_Form_Subform_3Filter

Formulare

Filter_Formular

Filter_Formular_Subformular_3Filter

Suche_Formular

Parametersuche_Suche_Formular_Subformular

Suche_Filter_Formular_Subformular

Standardfilter_Formular_Subformular

Standardfilter_Formular_Subformular_Datensatzkorrektur

Suche und Filtern von Daten mit Makros erweitert

Abfragen

Listenfeld Medienart

Listenfeld Kategorie

Listenfeld Verfasser

Filter_Ansicht

Filter_Such_Ansicht

Direkte Filterung

Formulare

Das Formular «Filter_Formular»

Makrosteuerung für das automatische Filtern

Das Formular «Filter_Formular_direkt_Nullwerte»

Makrosteuerung für die direkte Filterung des Formulars

Das Formular «Filter_Formular_Subformular»

Das Formular «Filter_Formular_Subformular_bedingende_Filter»

Makrosteuerung für die bedingte Filterung des Formulars

Das Formular «Filter_Formular_Subformular_bedingende_Filter_allround»

Makrosteuerung für die bedingte Allround-Filterung des Formulars

Das Formular «Filter_Formular_Subformular_bedingende_Filter_Datensaetze»

Das Formular «Suche_Formular»

Makrosteuerung für die Aktualisierung des Formulars

Das Formular «Suche_Formular_Subformular»

Das Formular «Suche_Filter_hierarchisch_allround»

Makrosteuerung für den Start des Formulars

Makrosteuerung für die hierarchische Filterung des Formulars

Das Formular «Suche_Filter_hierarchisch_allround_Datensaetze»

Makrosteuerung für die hierarchische Filterung des Formulars

Aktuelle Standardwerte für Datum und Zeit setzen

Tabellen

Abfragen

DatumZeitvorgabe

Zeitdifferenzen

Zeitdifferenzen_Formularvorlage

Formulare

Aktuelles Datum oder aktuelle Zeit über Abfrage einsetzen

Aktuelles Datum oder aktuelle Zeit über SQL-Default-Wert

Makro für einen neuen Zeitstempel über SQL

Aktuelles Datum oder aktuelle Zeit vollautomatisch über Makros

Makros zum vollautomatischen Einsetzen von aktuellem Datum und aktueller Zeit

Aktuelles Datum oder Zeitstempel über den Standardwert des Formularfeldes

Makros zur Erstellung des Standardwertes des Datums oder Zeitstempels

Aktuelles Datum oder aktuelle Zeit mit Zeitdifferenzberechnung

Makros für einen neuen Zeitstempels

Berichte

Zeitdifferenzen in Minuten

Zeitdifferenzen berechnet in einer Abfrage in Stunden

Fortlaufende gruppierte Summierungen erstellen

Tabellen

Abfragen

Saldo

Ansicht_Kasse_mit_Umbuchungen

Kontoverlauf

Kategorieverlauf

Saldo_Konto

Saldo_Kategorie

Datum_Max

monatlich_Konto

monatlich_Kategorie

Konto_Saldo_Konto_Kategorie_Adressat

Konto_Saldo_Konto_Kategorie_Adressat_Umbuch

Formulare

Konto

Kasse

Konto_Salden_separat

Konto_Salden

Konto_Salden_komplett

Konto_Salden_komplett_Umbuchung

Berichte

Fortlaufendes Saldo mit Splitbuchungen

Tabellen

Abfragen

Saldo

Ansicht_Kasse_mit_Umbuchungen

Ansicht_Kasse_Kategorie

Kontoverlauf

Kategorieverlauf

Saldo_Konto

Saldo_Kategorie

Datum_Max

monatlich_Konto

monatlich_Kategorie

Konto_Saldo_Konto_Kategorie_Adressat

Konto_Saldo_Konto_Kategorie_Adressat_Umbuch

Formulare

Konto

Kasse

Konto_Salden

Konto_Salden_komplett

Konto_Salden_komplett_Umbuchung

Buchung_Umbuchung_Salden

Berichte

Autotext, Markierung von Suchergebnissen und Rechtschreibprüfung

Tabellen

Abfragen

Formulare

Autotext

Suche_markieren

Rechtschreibkontrolle_hinterher

Rechtschreibkontrolle_direkt

Terminübersicht am Beispiel einer Ferienhausvermietung

Tabellen

Abfragen

Ansicht_Datum

Ansicht_Datum_lfdNr

Belegung

Bericht

Formular

Bericht

Rechnungen über Berichte erstellen

Tabellen

Abfragen

Ansicht Bericht_Ware_gruppiert

Abfrage Verkauf_berechnet

Formular

Berichte

Register in einem Formular

Tabelle

Formulare

Registerformular

Makrosteuerung für das einfache Registerformular

Registerformular_Grafik

Makrosteuerung für Registerformular mit Umschaltbuttons

Registerformular_Grafikbuttons

Makrosteuerung für Registerformular mit grafischer Registerdarstellung

Registerformular_mehrere_Buchstaben_Grafik

Makrosteuerung für das Registerformular mit Umschaltbuttons für mehrere Buchstaben

Registerformular_mehrere_Buchstaben_Grafikbuttons

Makrosteuerung für das Registerformular mit grafischer Registerdarstellung für mehrere Buchstaben

Duplizieren von Daten

Tabellen

Abfragen

Formulare

Datenübertragung ohne Makroeinsatz

Datenübertragung mit Makrounterstützung

Defaultwerte

Terminkalender führen

Tabellen

Abfragen

Ansicht_Datum

Ansicht_Feiertage

Ansicht_Datum_Feiertage

Ansicht_Datum_Feiertage_Ferien

Ansicht_Datum_Termine

Ansicht_komplett

Abfrage_Halbjahreskalender

Datum_Termine

Datum_Vorgabe_Termine

Wochenuebersicht_Formular

Wochenuebersicht_komplett

Formulare

Kalender_Termine

Termine_Kalender

Berichte

Halbjahreskalender

Monatskalender

 

 

Wozu Datenbankbeispiele?

Für viele Nutzer ist die Hürde beim Erstellen von Datenbanken recht hoch. Schon die Erstellung von Tabellen, ihre Verknüpfung sowie die Beschickung in einfachen Formularen stellt häufig ein Problem dar.

Diese Probleme sollte allerdings das LO-Handbuch «Erste Schritte» sowie das Base-Handbuch bewältigen helfen. Diese Beispiele ergänzen vor allem das Base-Handbuch. Die Beispiele sind meist speziell auf eine Problemstellung hin ausgerichtet. Meist stellen sie, im Gegensatz zu den Mediendatenbanken für das Base-Handbuch, keine komplette Lösung für eine Datenbank dar. Die Reihenfolge der Beispiele ist nicht vom Einfachen zum Komplizierten gewählt. Jedes Kapitel steht für sich. Es kann aber sein, dass in nachfolgenden Kapiteln auf bestimmte Konstruktionen des vorangegangenen Kapitel Bezug genommen wird. In diesem Falle also bitte einfach die entsprechende Stelle aufsuchen.

Diese Beschreibung deckt bisher nicht alle Beispieldatenbanken ab, die mit dem Base-Handbuch weiter gegeben werden. Die Beschreibung soll daher ständig weiter vervollständigt werden.

Zeitmessung in einer Datenbank

Zeitmessungen könnten z. B. für die Durchführung von Sportveranstaltungen genutzt werden. Gedacht ist hier nicht an große Veranstaltungen, wo so etwas sowieso elektronisch erfolgt. Vielmehr könnte es um Veranstaltungen im Schulbereich gehen.

Der PC ersetzt hier die Stoppuhr und das Niederschreiben der Ergebnisse. Er kann außerdem auch die Auswertung erstellen sowie Urkunden drucken. Im Vordergrund soll hier die Funktion der Stoppuhr stehen.

Um mit einer Datenbank auch Zeitmessungen durchführen zu können werden in geringem Umfang Makrokenntnisse gebraucht.

Tabellen

Die Datenbank besteht im sparsamsten Aufbau aus zwei Tabellen.

In der Tabelle "Start" werden folgende Felder definiert:

Feldname

Feldtyp

Beschreibung

ID

Integer

Primärschlüssel der Tabelle. Der Primärschlüssel muss eindeutig sein. Das Feld kann auch als Auto-Wert-Feld gesetzt werden.

Startzeit

Datum/Zeit

Hier wird Tag und Uhrzeit des vorgesehenen Startes festgehalten. Personen, die zusammen starten, haben die gleiche Startzeit.

Name

Text

Der Name der Person, die startet. Bei Schulwettkämpfen wären hier Zusätze wie Klasse,  Schule oder gegebenenfalls auch Geburtsdaten notwendig.

Dauer

Integer

Die Dauer wird in Sekunden oder Millisekunden gemessen. Es handelt sich dabei um einen Zahlenwert ohne Nachkommastellen, keinen Wert wie bei einer Uhrzeit.

Zeit (nachträglich eingefügt)

Datum/Zeit

Die Zeitdauer wird hier als Zeit im Format Minuten:Sekunden,Hundertstelsekunden aus dem Formular «Einzelmessung_Zeitfeld» übernommen. Der Datumsteil wird durch ein Makro hinzugefügt.

In der Tabelle "Zeitspeicher" werden die folgenden Felder definiert:

Feldname

Feldtyp

Beschreibung

Startzeit

Datum/Zeit

Die Startzeit ist Primärschlüssel dieser Tabelle. Sie ist gleichzeitig Fremdschlüssel der Tabelle "Start". Damit wird für alle Personen, die die gleiche Startzeit haben, auch bei der Durchführung der einheitliche Start festgelegt.

Zeitspeicher

Integer

Hier wird ein interner Basic-Wert für die tatsächliche Startzeit abgespeichert. Der Wert wird erst dann eingefügt, wenn der Button «Start» betätigt wurde.

Beide Tabellen werden miteinander in den Beziehungen verbunden. Es muss also eine Startzeit festgelegt werden, bevor eine Person in die Startliste übertragen werden kann.

Die Beziehung zwischen den Tabellen wird mit einem rechten Mausklick auf die Verbindungslinie genauer definiert. Wird im "Zeitspeicher" eine "Startzeit" geändert, so werden alle "Startzeit"-Einträge in der Tabelle "Start" entsprechend angepasst. Dies ist über die Update Optionen → Kask. Update gewährleistet. Wird eine "Startzeit" aus der Tabelle "Zeitspeicher" gelöscht, so sollen Einträge, die sich aus der Tabelle "Start" genau auf die gelöschte "Startzeit" beziehen, geleert werden. Dies wird über die Löschoptionen → Null setzen erreicht.

Die tatsächlichen Startzeiten müssen nicht mit der eingetragenen Startzeit übereinstimmen. Auch wenn eine eingetragene Startzeit z. B. 10:00 Uhr ist, kann tatsächlich um 15:13 Uhr gestartet werden und trotzdem ein genaues Messergebnis erwartet werden.

Zusätzlich gibt es eine Tabelle "Filter". Diese Tabelle dienst nur dazu, einen Filterwert aus einem Hauptformular an ein Unterformular weiter zu geben.

Feldname

Feldtyp

Beschreibung

ID

Ja/Nein

Die Filtertabelle soll nur einen Datensatz abspeichern. So lässt sie sich auch problemlos in Abfragen als Datenquelle für eine Unterabfrage nutzen.

Start_ID

Integer

Hier wird der Primärschlüssel der Tabelle "Start" gespeichert. Das ist im Formular der Wert für den Zieleinlauf. Die Eingabe einer Startnummer sollte beim Abspeichern die Zeit mit speichern können. Dies soll über die Filtertabelle erreicht werden.

Formulare

Die Messung erfolgt innerhalb von Formularen. Ein Formular zeigt nur die Messung eines Wertes. In den folgenden Formularen werden dann auch Mitbewerber angezeigt. Schließlich wird eine komplette Zeitmessung für ein Rennen ausgestellt, bei dem gleichzeitig gestartete Teilnehmer in eine Rangliste eingeordnet werden. Hier zuerst einmal die einfachste Variante eines Formulars mit einfachen Feldern.

Das Formular «Einzelmessung»

Das Formular sieht erst einmal recht karg aus. Die ursprünglich geplante Startzeit ist zusammen mit dem Datum als Zeitstempel abgespeichert. Da für die Datenbank über die oben genannten 2 Tabellen auch andere Funktionen gewährleistet werden sollen, kann hier nur dann eine Startzeit/Startdatum angegeben werden, wenn die entsprechende Zeit bereits in der anderen Tabelle vorhanden ist. Deshalb ist für die Startzeit ein Listenfeld vorgesehen. Das Listenfeld liest die "Startzeit" aus der Tabelle "Zeitspeicher" und trägt genau diesen Wert in die Tabelle "Start" als "Startzeit" ein. Für die Messung ist diese Zeit wegen der Abspeicherung der Startzeit in der zweiten Tabelle zwingend notwendig.

Start startet die Zeitmessung. Mit Stop wird die Zeitmessung beendet und die gemessene Zeit in dem Feld «Dauer» abgespeichert.

Die Entwurfsansicht des Formulars gibt etwas genauer Aufschluss darüber, wie die Zeit ermittelt wird.

In dem Formular befindet sich ein Unterformular mit dem Namen «Zeitspeicher». Dieses Unterformular ist verbunden mit der Tabelle "Zeitspeicher". Das als unsichtbar definierte Element «Hidden» ist mit dem Datenfeld "Zeitspeicher" verbunden. Formular und Unterformular sind über die "Startzeit" verbunden.

Der Button Start liegt in dem Unterformular. Durch den Formularwechsel vom Hauptformular zum Unterformular wird beim Betätigen des Start-Buttons also der Inhalt des Hauptformulars abgespeichert. Wird Start betätigt, so schreibt ein Makro die interne Startzeit in das versteckte Feld.

Der Button Stop könnte auch im Unterformular liegen. Die Abspeicherung des gemessenen Wertes wird schließlich per Makro erledigt. Wird Stop betätigt, so wird über das Makro die Differenz zwischen dem Wert in dem versteckten Feld und dem momentanen Wert ermittelt und in das Feld "Dauer" übertragen. Die Messung erfolgt in Millisekunden.

Makros für das Formular «Einzelmessung»

Der Button Start startet über das Ereignis «Aktion ausführen» das Makro «Zeitmessung_Start».

SUB Zeitmessung_Start

        DIM oDoc AS OBJECT

        DIM oDrawpage AS OBJECT

        DIM oForm AS OBJECT

        DIM oFeld AS OBJECT

        DIM lZeit AS LONG

Zuerst werden die Variablen benannt und mit den ihnen entsprechenden Typen versehen. Bis auf eine Variable sind alle vom Typ «Object». Die Zeitangabe wird hingegen als Zahl gespeichert. Der Typ «Long» entspricht dem größten Zahlentyp ohne Nachkommastellen in StarBasic. Er entspricht vom Umfang her einem Integer-Feld der Datenbank.

        oDoc = thisComponent

        oDrawpage = oDoc.drawpage

        oForm = oDrawpage.forms.getByName("Formular")

        oSubForm = oForm.getByName("Zeitspeicher")

        oFeld = oSubForm.getByName("Hidden")

Das Feld «Hidden» wird aufgesucht. Es soll den internen Wert für die Startzeit speichern. Das Feld wird über das Formular und das darin liegende Unterformular erreicht. Die angegebenen Namen entsprechen den Namen, die im Formularnavigator sichtbar sind.

        'lZeit = Timer()        ' Gibt die Systemzeit in Sekunden an

        lZeit = getSystemTicks() ' Gibt die Systemzeit in Millisekunden an

Die Zeitspeicherung kann entweder in Sekunden oder in Millisekunden vorgenommen werden. Hier ist die Variante für die Millisekunden ausgewählt. Die Systemzeit wird ausgelesen.

        oFeld.BoundField.UpdateInt(lZeit)

        oSubForm.UpdateRow()

END SUB

Anschließend wird die Systemzeit in das Feld «Hidden» übertragen. BoundField bedeutet hier das an dieses Feld gebundene Feld der Tabelle, in der der Wert abgespeichert werden soll. UpdateInt heißt, dass dieses Feld einen Integer-Wert abspeichern wird. Es ist in der Tabelle jetzt das Feld auf einen neuen Integer-Wert gesetzt worden. Dieser Wert ist jetzt in dem Feld der Tabelle enthalten und muss mit UpdateRow über das entsprechende Formular noch abgespeichert werden.

Der Button Stop startet über das Ereignis «Aktion ausführen» das Makro «Zeitmessung_Ende».

SUB Zeitmessung_Ende

        DIM oDoc AS OBJECT

        DIM oDrawpage AS OBJECT

        DIM oForm AS OBJECT

        DIM oFeld AS OBJECT

        DIM oFeld1 AS OBJECT       

        DIM lZeit AS LONG       

        DIM lZeit1 AS LONG

        oDoc = thisComponent

        oDrawpage = oDoc.drawpage

        oForm = oDrawpage.forms.getByName("Formular")

        oSubForm = oForm.getByName("Zeitspeicher")

        oFeld = oSubForm.getByName("Hidden")

        oFeld1 = oForm.getByName("Dauer")

Die Ansprache der Felder im Formular ist gleich. Jetzt wird allerdings noch ein zusätzliches das Feld «Dauer» angesteuert, in dem schließlich die gemessene Zeit gespeichert werden soll.

        'lZeit1 = Timer()        ' Gibt die Systemzeit in Sekunden an

        lZeit1 = getSystemTicks() ' Gibt die Systemzeit in Millisekunden an

        lZeit = oFeld.getCurrentValue

        oFeld1.BoundField.UpdateInt(lZeit1-lZeit)

        oForm.UpdateRow()

END SUB

Es wird die aktuellen Zeit, wie im vorhergehenden Makro, ausgelesen. Dann wird der gespeicherte Startwert aus dem Feld «Hidden» ausgelesen. Der Startwert wird von dem aktuellen Wert subtrahiert. Das Ergebnis der Subtraktion wird in das Feld «Dauer» übertragen und anschließend über das entsprechende Formular abgespeichert.

Das Formular «Startzeit_Gruppe»

 

Dieses Formular ermöglicht es, neue Startzeiten einzugeben. Den Startzeiten werden im Unterformular die Personen zugeordnet, die zu der jeweiligen Zeit an den Start gehen wollen. Hier ist z.B. eine Gruppe von 3 Personen am 1.5.14 um 10:00 Uhr gemeinsam gestartet. Die Zeit, die die einzelnen Personen für die Strecke benötigt haben, wird unter «Dauer» erfasst.

Im Hauptformular wird auf die Tabelle "Zeitspeicher" zugegriffen. In ihr wird die "Startzeit" und auch die systeminterne Startzeit (in dem Feld "Zeitspeicher") abgespeichert. Die systeminterne Startzeit braucht nicht für den Nutzer sichtbar zu erscheinen. Das Feld mit der Bezeichnung «Hidden» ist deshalb einfach irgendwo auf dem Formular untergebracht.

Über Start wird dieses Feld mit der systeminternen Startzeit versorgt.

Die Tabelle "Start" liegt jetzt im Unterformular. In ihr wird auch der Zieleinlauf des Rennens gespeichert. Verbindungsglied zwischen Hauptformular und Unterformular ist das Feld "Startzeit". Die Zeit, die die jeweilige Person für die Strecke benötigt hat, wird in dem außerhalb des Tabellenkontrollfeldes versteckten Feld «Dauer» abgespeichert. Dieses Feld ist über Makros erst einmal einsichtiger zu erreichen als ein Feld innerhalb des Tabellenkontrollfeldes. Gleichzeitig zeigt es auch, dass Werte des aktuellen Datensatzes des Tabellenkontrollfeldes außerhalb des Tabellenkontrollfeldes angezeigt oder eingegeben werden können. So etwas kann z.B. auch für Textfelder mit viel Inhalt oder auch für abgespeicherte Bilder sinnvoll sein.

Makros für das Formular «Startzeit_Gruppe»

SUB Zeitmessung_Massenstart

        DIM oDoc AS OBJECT

        DIM oDrawpage AS OBJECT

        DIM oForm AS OBJECT

        DIM oFeld AS OBJECT

        DIM lZeit AS LONG

        oDoc = thisComponent

        oDrawpage = oDoc.drawpage

        oForm = oDrawpage.forms.getByName("Zeitspeicher")

        oFeld = oForm.getByName("Hidden")

        'lZeit = Timer()        ' Gibt die Systemzeit in Sekunden an

        lZeit = getSystemTicks() ' Gibt die Systemzeit in Millisekunden an

        oFeld.BoundField.UpdateInt(lZeit)

        IF oForm.RowInserted() THEN

                oForm.InsertRow()

        ELSE

                oForm.UpdateRow()

        END IF

END SUB

Da der Formularaufbau (Hauptformular zu Unterformular) umgedreht wurde müssen die Felder entsprechend anders angesprochen werden. Im Hauptformular wird jetzt der Start eingetragen. Es wäre zwar sinnlos, einen Start ohne entsprechend zugeordnete Personen ablaufen zu lassen. Trotzdem soll hier einfach über Start auch abgesichert werden, dass es bei einer Neueingabe von Daten nicht zu einer Fehlermeldung kommt. Aus diesem Grunde wird abgefragt, ob es sich um eine eingefügten Datensatz handelt (oForm.RowInserted). Ist der Datensatz neu einzufügen, dann müssen die Daten an die Tabelle mit oForm.InsertRow weitergegeben werden. Ansonsten handelt es sich um eine Datenänderung, also oForm.UpdateRow.

SUB Zeitmessung_Ende_Massenstart

        DIM oDoc AS OBJECT

        DIM oDrawpage AS OBJECT

        DIM oForm AS OBJECT

        DIM oFeld AS OBJECT

        DIM oFeld1 AS OBJECT       

        DIM lZeit AS LONG       

        DIM lZeit1 AS LONG

        oDoc = thisComponent

        oDrawpage = oDoc.drawpage

        oForm = oDrawpage.forms.getByName("Zeitspeicher")

        oSubForm = oForm.getByName("Formular")

        oFeld = oForm.getByName("Hidden")

        oFeld1 = oSubForm.getByName("Dauer")

        'lZeit1 = Timer()        ' Gibt die Systemzeit in Sekunden an

        lZeit1 = getSystemTicks() ' Gibt die Systemzeit in Millisekunden an

        lZeit = oFeld.getCurrentValue

        oFeld1.BoundField.UpdateInt(lZeit1-lZeit)

        oSubForm.UpdateRow()

END SUB

Die Zeitmessung wird genau so wie im ersten Formular gestoppt. Unterschied zwischen den Makros ist hier lediglich die entsprechende Abfrage der Felder. Das Feld «Dauer» liegt dabei im Unterformular. Um das Feld innerhalb des Tabellenkontrollfeldes anzusprechen wäre noch eine zusätzliche Zeile notwendig gewesen. Der Zugriff über Namen kann dabei nur funktionieren, wenn unterschiedliche Namen vergeben wurden. Ansonsten müssen Felder über getByIndex angesprochen werden.

Das Formular «Nur_Start»

Bei den bisherigen Konstruktionen kann es ohne weiteres passieren, dass ein Start mehrmals hintereinander eingegeben, also die Zeit nicht korrekt gemessen wird. Dies lässt sich vermeiden, wenn nach dem erfolgten Start ein erneutes Beschreiben des Start-Wertes unmöglich gemacht wird.

Aus dem folgenden Formular verschwinden nach dem Start die Datensätze der Personen, die zur gleichen Startzeit unterwegs sind.

Die Formularansicht ist gegenüber dem Formular «Startzeit_Gruppe» etwas reduziert. Natürlich gibt es in einem reinen Start-Formular keinen Stop-Button. Außerdem werden nur die Namen der Personen aufgelistet, die zu der entsprechenden Zeit am Start stehen müssen. Die «Dauer», die sie unterwegs sind, wird in diesem Formular nicht erfasst.

Auch der Formularnavigator zeigt nur eine reduzierte Ansicht des Formulars «Startzeit_Gruppe». Elemente für den Zieleinlauf fehlen.

Als Grundlage für das Formular «Zeitspeicher» fungiert hier allerdings nicht die Tabelle "Zeitspeicher", sondern eine Abfrage:

Die Abfrage bezieht sich auf die Tabelle "Zeitspeicher". Sie zeigt alle Felder dieser Tabelle an (im ersten Feld des grafischen Editors steht: Zeitspeicher.*). Da sich die Abfrage nur auf diese eine Tabelle bezieht ist sie so auf jeden Fall editierbar.

Im zweiten Feld wird definiert, dass "Zeitspeicher" auf jeden Fall leer sein soll, wenn der Datensatz angezeigt wird. Alle Felder mit ausgefülltem "Zeitspeicher" erscheinen also nicht. Die so formulierte Abfrage lautet schließlich:

SELECT * FROM "Zeitspeicher" WHERE "Zeitspeicher" IS NULL

Diese recht einfache Abfrage hätte auch direkt im Formular «Zeitspeicher» als Filterwert eingegeben werden können:

rechte Maustaste über dem Formularnamen «Zeitspeicher» → Eigenschaften → Daten → Filter

In das freie Feld kann eingetragen werden: ("Zeitspeicher" IS NULL)

Makros für das Formular «Nur_Start»

Das Formular «Nur_Start» nutzt das Makro «Zeitmessung_Massenstart», das bereits im vorangegangenen Formular über den Button Start ausgelöst wurde.

Das Formular «Nur_Ziel»

Dieses Formular muss wesentlich mehr Funktionen erfüllen als die vorhergehenden Formulare. Es soll dazu dienen, nur nach Auswahl der Startnummer den Zieldurchgang zu regeln. Dabei soll es anzeigen, welche Zeit die gerade das Ziel passierte Person benötigt hat. Außerdem soll noch die Einordnung in Form einer Rangliste erfolgen.

 

Hier ist gerade Karl Müller als 4. Person ins Ziel gekommen. Die Startnummer ("ID") von Karl Müller wurde aus dem Listenfeld direkt neben dem Button Stop ausgewählt, als sich Karl Müller dem Ziel näherte. Stop wurde gedrückt, der Name zu der Startnummer und die Zeit wurde angezeigt, gleichzeitig die Positionierung in dem Feld der Personen, die zur gleichen Zeit gestartet waren. Das Listenfeld zeigt die Startnummer «1» von Karl Müller jetzt nicht mehr an, da Karl Müller durchs Ziel gekommen ist.

 

Der Formularnavigator zeigt gleich drei Formulare. Das erste Formular «Formular» enthält nur ein Listenfeld für die «Startnummern». Es ist von den anderen Formularen getrennt, liegt also neben dieses Formularen auf der Benutzeroberfläche. Die Anzeige von «Formular» soll keine Auswirkung auf die anderen Formulare haben. Schließlich ist ein Wert, der im Listenfeld «Startnummern» noch verzeichnet ist, nicht in den anderen Formularen zu finden.

Das Formular «Formular» hat als Datengrundlage die Tabelle "Filter" angegeben. Das Formular ist nur zur Änderung von Daten vorgesehen. Es sollen keine Daten gelöscht werden und keine neuen Datensätze angelegt werden. Die Tabelle "Filter" besteht so nur aus einem Datensatz.

Das Listenfeld «Startnummer» zeigt den gleichen Wert an, den es auch an die darunterliegende Tabelle weitergibt. Die Datenquelle für das Listenfeld ist auf SQL eingestellt.

SELECT "Start"."ID", "Start"."ID"

 FROM "Start", "Zeitspeicher"

 WHERE "Start"."Startzeit" = "Zeitspeicher"."Startzeit"

  AND "Zeitspeicher"."Zeitspeicher" IS NOT NULL

  AND "Start"."Dauer" IS NULL

Diese Abfrage stellt zweimal das gleiche Feld "Start"."ID". Das ist notwendig, wenn in den Eigenschaften des Listenfeldes → Daten Gebundenes Feld = 1 steht. In neueren Versionen von LO kann mit der Einstellung Gebundenes Feld = 0 auch nur ein Feld angegeben werden.

Die Abfrage stellt zuerst einmal die Beziehung zwischen den Tabellen "Start" und "Zeitspeicher" her. Dann muss die Bedingung erfüllt sein, dass "Zeitspeicher"."Zeitspeicher" nicht leer ist. Das bedeutet, dass eben ein Start bereits erfolgt ist. Und schließlich sollen nur die Datensätze erscheinen, bei denen in "Start"."Dauer" bisher kein Eintrag existiert. Datensätze von Personen, deren Zeit schon gemessen wurde, fallen also auch heraus.

Das Formular «Ziel» hat als Datengrundlage eine Abfrage, und zwar "Abfrage_Ziel". Diese Abfrage wird zusätzlich über Formular-Eigenschaften → Daten → Filter eingeschränkt, so dass nur der aktuelle Datensatz angezeigt wird, der dem Eintrag in der Tabelle "Filter" entspricht:

("a"."ID" = (SELECT "Start_ID" FROM "Filter" WHERE "ID" = TRUE))

Das Unterformular «Platz» greift auf die gleiche Abfrage zu. Hier ist die Filterung dann allerdings anders angelegt. Unter Formular-Eigenschaften → Daten → Filter steht:

( "a"."Dauer" IS NOT NULL )

Es werden nur die Werte aus der Abfrage angezeigt, die einen Eintrag im Feld "a"."Dauer" haben. Es werden also nur die Personen angezeigt, die auch tatsächlich mit einer entsprechend vermerkten Zeit das Ziel erreicht haben.

Unter Formular-Eigenschaften → Daten → Sortierung steht:

"Platz" ASC

Dies bedeutet, dass Datensätze nach der erreichten Platzierung, vermerkt im Feld "Platz", angezeigt werden sollen.

Die Abfrage, auf die sich die Formulare «Ziel» und «Platz» beziehen, sieht im SQL-Code so aus:

SELECT "a".*,

 ( SELECT "Zeitspeicher" FROM "Zeitspeicher" WHERE "Startzeit" =   "a"."Startzeit" ) AS "Zeitspeicher",

 ( SELECT COUNT( "ID" ) FROM "Start" WHERE "Dauer" <= "a"."Dauer"  AND "Startzeit" = "a"."Startzeit" ) AS "Platz"

 FROM "Start" AS "a"

 WHERE "Zeitspeicher" IS NOT NULL

Bei dieser Abfrage handelt es sich um eine korrelierte Unterabfrage. Innerhalb der Abfrage wird auf Werte der aktuellen Abfrage Bezug genommen. Um so eine korrelierte Abfrage zu erstellen muss der Tabelle, auf der die äußere Abfrage beruht, ein Alias zugewiesen werden: "Start" AS "a".

Mit "a".* werden alle Felder der Tabelle "Start" angezeigt. Die Abfrage erfüllt also eine wichtige Voraussetzung dafür, dass sie editierbar ist, indem der Primärschlüssel aus der Tabelle "Start" auch in der Abfrage auftaucht.

Der "Zeitspeicher" wird hier durch eine Unterabfrage abgerufen, da sonst die Abfrage nicht mehr editierbar wäre. Die Unterabfrage sucht in der Tabelle "Zeitspeicher" nach dem Wert für das Feld "Zeitspeicher". Der Wert muss mit der "Startzeit" des aktuellen Datensatzes von "Start", also "a"."Startzeit", übereinstimmen. Das trifft jeweils auf höchstens einen Datensatz zu und kann also in einer Unterabfrage verwandt werden. Unterabfragen, die mehrere Datensätze ergeben, können in Feldern einer Abfrage nicht genutzt werden.

Die zweite korrelierte Unterabfrage ermittelt die aktuelle Platzierung der im jeweiligen Datensatz repräsentierten Person. Es werden alle Einträge im Feld "ID" gezählt. Die "Dauer" dieser Einträge muss allerdings kleiner oder gleich der Dauer des aktuellen Datensatzes, also "a"."Dauer" sein. Außerdem muss die "Startzeit" gleich der Startzeit des aktuellen Datensatzes also "a"."Startzeit" sein.

Mit dieser Abfrage "Abfrage_Ziel" lässt sich der gesamte Zieldurchlauf mit Platzierungen darstellen. Wird für die Dauer des Rennens kein Wert im Millisekunden-Bereich benötigt, sondern z.B. nur im Zehntelsekunden-Bereich, so kann in so einer Abfrage entsprechend nachgebessert werden.

Makro für das Formular «Nur_Ziel»

SUB Zeitmessung_nur_Ziel

        DIM oDoc AS OBJECT

        DIM oDrawpage AS OBJECT

        DIM oForm AS OBJECT

        DIM oForm2 AS OBJECT

        DIM oSubForm2 AS OBJECT

        DIM oFeld AS OBJECT

        DIM oFeld1 AS OBJECT       

        DIM lZeit AS LONG

        DIM lZeit1 AS LONG

        oDoc = thisComponent

        oDrawpage = oDoc.drawpage

        oForm = oDrawpage.forms.getByName("Formular")

        oForm2 = oDrawpage.forms.getByName("Ziel")

        oSubForm2 = oForm2.getByName("Platz")

        oFeld = oForm2.getByName("Hidden")

        oFeld1 = oForm2.getByName("Dauer")

        oForm.UpdateRow()

        oForm2.Reload()

        oForm2.Last()

        'lZeit1 = Timer()        ' Gibt die Systemzeit in Sekunden an

        lZeit1 = getSystemTicks() ' Gibt die Systemzeit in Millisekunden an

        lZeit = oFeld.getCurrentValue

        oFeld1.BoundField.UpdateInt(lZeit1-lZeit)

        oForm2.UpdateRow()

        oForm.getByName("Startnummern").Refresh()       

END SUB

Zuerst werden die verschiedenen Formularfelder, wie vorher bereits, zugänglich gemacht. Dann wird bei Betätigung des Buttons Stop das Formular "Formular" mit oForm.UpdateRow abgespeichert. Anschließend wird das Formular "Ziel" mit oForm2.Reload neu geladen. Da hier der Datensatzzeiger in LibreOffice 4.3.0.0 Beta1  (aus nicht nachvollziehbaren Gründen) nicht auf den letzten Datensatz springt, sondern einen neuen Datensatz erstellen will, wird zusätzlich der Datensatzzeiger des Formulars auf den letzten (einzigen) Datensatz eingestellt: oForm2.Last.

Das Auslesen und Eintragen des Zeitmesswertes erfolgt wie bereits bei den anderen Makros erklärt. Gleiches gilt auch für die entsprechende Abspeicherung.

Schließlich wird noch das Listenfeld für die Startnummern neu eingelesen, da ja jetzt genau der Datensatz fehlt, den es vorher angezeigt hatte: oForm.getByName("Startnummern").Refresh().

Das Formular «Einzelmessung_Zeitfeld»

Dieses Formular unterscheidet sich von dem Formular «Einzelmessung» äußerlich nur gering. Statt der Angabe einer Zeit in Millisekunden erfolgt hier die Zeitangabe in der Schreibweise Minuten:Sekunden,Hundertstelsekunden.

Um diese Zeitangabe entsprechend speichern zu können muss zuerst einmal der Tabelle «Start» ein zusätzliches Feld «Zeit» hinzugefügt werden. Dieses Feld muss als «Datum/Zeit [TIMESTAMP]» - Feld definiert werden. Die reinen Zeitfelder sind nicht in der Lage, Zeiten im Bereich unterhalb einer Sekunde zu speichern. Leider sind aber auch die TIMESTAMP-Felder von der Standardeinstellung in Base nicht dafür vorgesehen. Hier muss entsprechend über SQL nachgeholfen werden. In Extras → SQL ist einzugeben:

ALTER TABLE "Start" ALTER COLUMN "Zeit" TIMESTAMP(6)

Jetzt ist das Feld in der Lage, auch im Millisekundenbereich Zeiten aufzunehmen.

Das nächste Problem für ein Formular besteht darin, dass so ein Feld sowohl ein Datum als auch eine Zeit abspeichert. Deshalb erstellt Base auch im Formularassistenten oder bei dem Weg über das direkte Hinzufügen von Feldern für ein Datum/Zeit – Feld zwei Eingabemasken. Eine dient zur Eingabe des Datums, eine andere zur Eingabe der Zeit. Diese ist für eine einfache Zeitangabe mit Nachkommastellen nicht brauchbar. Stattdessen muss ein Feld her, dass beides darstellen könnte und außerdem in der Lage ist, auch noch die Hundertstel Sekunden mit aufzunehmen. Deshalb wird das Feld als «Formatiertes Feld» erstellt. Dieses Feld wird hier nur zur Anzeige benutzt und deswegen in Eigenschaften → Allgemein → Nur Lesen auf «Ja» gestellt. Zur Eingabe von Werten ist es so auf dem direkten Wege nicht geeignet, da neben der Zeiteingabe eine Datumseingabe erwartet wird, das Feld aber bei der vorliegenden Formatierung diese Datumseingabe nicht mitliefert.

Die Ermittlung der korrekten Zeit muss wieder über ein Makro geleistet werden.

Makro für das Formular «Einzelmessung_Zeitfeld»

SUB Zeitmessung_Timestamp

        DIM unoStmp AS NEW com.sun.star.util.DateTime

        DIM daStart AS NEW com.sun.star.util.DateTime

        DIM oDoc AS OBJECT

        DIM oDrawpage AS OBJECT

        DIM oForm AS OBJECT

        DIM oFeld AS OBJECT

        DIM oSubForm AS OBJECT

        DIM oSubFeld AS OBJECT

        DIM stZeit AS STRING

        DIM ar()

        DIM arMandS()

        DIM loNano AS LONG

        DIM inSecond AS INTEGER

        DIM inMinute AS INTEGER

        DIM inIndex AS INTEGER

        DIM lZeit AS LONG

        DIM lZeit1 AS LONG

Sämtliche Variablen werden zuerst deklariert. Hier ist darauf zu achten, dass die entsprechenden Zuweisungen für die Timestamp-Felder und für die Sekundenbruchteile, die Sekunden und Minuten stimmen. Die Zeitdefinition für die Sekundenbruchteile wird über das Struct von LibreOffice auf Nanosekunden festgelegt und benötigt daher eine Ganzzahl, die entsprechend groß genug ist, also vom Typ Long.

        oDoc = thisComponent

        oDrawpage = oDoc.Drawpage

        oForm = oDrawpage.forms.getByName("Formular")

        oColumns = oForm.Columns

        inIndex = oForm.findColumn("Startzeit")

        daStart = oForm.getTimestamp(inIndex)

Das Formular wird angesteuert. Der Timestamp für den Start wird aus der dem Formular zugrundeliegenden Tabelle ausgelesen, damit aus diesem Feld der Datumsteil für das Feld «Zeit» ausgelesen werden kann, in dem schließlich ein kompletter Timestamp gespeichert werden muss.

Alternativ kann auch einfach das Standardstartdatum 1899-12-30 eingesetzt werden. Schließlich wird der Datumswert für die Zeitauswertung nicht benötigt.

        oFeld = oForm.getByName("Zeit")

        oSubForm = oForm.getByName("Zeitspeicher")

        oSubFeld = oSubForm.getByName("Hidden")

        lZeit1 = getSystemTicks()

        lZeit = oSubFeld.getCurrentValue

        doZeit = (Cdbl(lZeit1-lZeit))/1000

Die Zeit wird in Sekunden umgerechnet. Damit auch Nachkommastellen angezeigt werden, muss die Differenz der Felder lZeit1-lZeit mit Cdbl in eine Double-Variable umgewandelt werden. Die Tausendstel erscheinen jetzt hinter dem Dezimalpunkt. Die Zeitangabe wird in einen Text umgewandelt und am Dezimalpunkt aufgetrennt.

        ar() = Split(Str(doZeit),".")

        loNano = Clng(ar(1)&"000000")

Die Tausendstel-Sekunden werden aus dem 2. Teil des Arrays ausgelesen. Es müssen noch 6 Nullen angehängt werden, da Nanosekunden gespeichert werden.

        doZeit = Fix(doZeit)

        inMinute = Fix(doZeit/60)

        inSecond = doZeitinMinute*60

Mit der Funktion Fix werden die Nachkommastellen der Zeitvariablen abgeschnitten. Für die Minutenangabe werden die verbleibenden Sekunden durch 60 dividiert. Die Sekunden werden ermittelt, indem der Rest aus der vorhergehenden Division bestimmt wird.

        WITH unoStmp

                .NanoSeconds = loNano

                .Seconds = inSecond

                .Minutes = inMinute

                .Hours = 0

                .Day = daStart.Day

                .Month = daStart.Month

                .Year = daStart.Year

        END WITH

        oFeld.BoundField.updateTimestamp(unoStmp)

        oForm.UpdateRow()

END SUB

Die einzelnen Variablen der Timestamps werden gesetzt. In manchen Anleitungen steht hierzu noch statt des Anteils NanoSeconds der Anteil HundrethSeconds. Für LibreOffice in der aktuellen Fassung ist diese Angabe nicht mehr korrekt.

Zum Schluss wird der Zeitstempel in das Anzeigefeld für die Zeit geschrieben und der Datensatz mit dem neuen Wert überschrieben.

Berichte

Eine direkte Auswertung der Abfrage "Abfrage_Ziel" stellt die Ergebnisliste dar. Die Felder sind bereits in der Abfrage errechnet, so dass nur noch eine Gruppierung nach der Startzeit und nach der Platzierung notwendig ist.

Im Entwurf sieht dieser Bericht so aus:

Damit die Bezeichnungen «Platz», «Name» und «Dauer» nur pro Rennen einmal erscheinen sind sie in dem Gruppenkopf für die Startzeit verankert worden.

Der Gruppenkopf für das Feld «Dauer» dient nur der Sortierung nach der Zeit. Hier könnte also genauso gut das Feld «Platz» zur Sortierung genutzt werden. Dieser Gruppenkopf wird in dem Bericht nicht angezeigt (Eigenschaften Gruppenkopf → Allgemein → Sichtbar: Nein)

Auf jeder Seite erscheint eine Seitennummerierung in der Form Seite … von … .

Auch ein Urkundendruck ist problemlos möglich. Sinnvoll ist hier allerdings, den Druckjob möglichst zu vereinfachen. Die einfachste Lösung wäre, nur die veränderlichen Felder im Bericht zu positionieren und auf einen schön gestalteten Urkundenvordruck zu drucken. Je einfacher der Bericht gehalten wird desto schneller baut sich schließlich auch das Fenster für den Druck auf. Und je weniger grafische Elemente eingefügt sind, desto kürzer ist auch die Druckzeit, die während irgendeiner Veranstaltung sowieso recht knapp ist.

Die Blattübersicht ist hier stark verkleinert. Schließlich ist der Druck «Urkunde» im Original auf eine Schriftart von 110 Punkten gesetzt worden.

In der verkleinerten Ansicht des Editors erscheint das Feld «Name» nicht richtig zentriert. Der Ausdruck zeigt aber eine entsprechenden saubere Zentrierung.

Für die Ausgabe der Platzierung ist eine kleine Formel erforderlich, da sonst die Zentrierung Probleme bereitet. Mit [Platz] wird das entsprechende Feld aus der Datenquelle angesprochen, mit &". Platz" wird an die Platzierung aus dem Datenfeld ein Punkt, ein Leerzeichen und der Begriff «Platz» angefügt.

Damit nur eine Urkunde pro Seite gedruckt wird ist bei Eigenschaften Detail → Allgemein → Seitenumbruch erzwingen: Nach Bereich ausgewählt.

Serienbriefe direkt aus Base heraus

Für Serienbriefe wird in der Regel zuerst einmal eine Writer-Datei gestartet und dann von dort aus der Seriendruck in die Wege geleitet. Die folgende Variante soll aufzeigen, wie so etwas auf verschiedene Art auch direkt aus einem Base-Formular heraus erfolgen kann.

Tabellen

Die Tabellen haben in dieser Datenbank nur die Funktion, für eine Bestückung der Serienbriefe eine Grundlage zu bieten. Sie werden deshalb nur wenig zusätzlich erklärt.

In der Tabelle "Anschrift" werden folgende Felder definiert:

Feldname

Feldtyp

Beschreibung

ID

Integer

Die ID ist Primärschlüssel dieser Tabelle. Sie ist als Autowert festgelegt.

Vorname

Text

 

Nachname

Text

 

Geschlecht

Text

Länge des Textes ist 1 Zeichen. Hier wird nur 'm' oder 'w' eingetragen, damit später daraus eine Anrede ermittelt werden kann.

Straße_Nr

Text

 

Postleitzahl

Text

Länge des Textes ist 5 Zeichen. Postleitzahlen sollten nicht als Zahlen abgespeichert werden. Führende Nullen sind sonst nicht möglich.

Ort

Text

 

Die Tabelle "Anschrift" zeigt das typische Anwendungsbeispiel eines Serienbriefes. Hier werden beispielhaft lediglich die Adressdaten in ein Druckdokument übertragen, das natürlich mit entsprechenden Feldern beliebig erweitert werden könnte.

Die weiteren Tabellen dienen dazu, auch den Druck einer Rechnung direkt aus der Datenbank heraus aufzuzeigen. Eine Rechnung unterscheidet sich in sofern von einem einfachen Serienbrief, da hier zu einem Datensatz (z.B. der Anschrift) viele Rechnungstitel gehören. Im Report-Builder wird so etwas durch Gruppierungen gelöst. Hier ist der eingeschlagene Weg etwas anders angefasst worden.

In der Tabelle "Rechnung" werden die folgenden Felder definiert:

Feldname

Feldtyp

Beschreibung

ID

Integer

Die ID ist Primärschlüssel dieser Tabelle. Sie ist als Autowert festgelegt. Der Primärschlüssel ist gleichzeitig die spätere Rechnungsnummer.

Datum

Datum

Hier wird das Rechnungsdatum festgelegt.

Anschrift_ID

Integer

Hier wird die "ID" der Tabelle "Anschrift" als Fremdschlüssel gespeichert. Grundsätzlich kann so eine Person mehrere Rechnungen erhalten ohne jedes Mal die gesamte Anschrift erneuern zu müssen.

Druckdatum

Datum

Beim Seriendruck von Rechnungen wird hier das Datum der Übertragung des Rechnungsinhaltes in ein *.pdf-Dokument gespeichert.

Die Tabelle Rechnungsinhalt wird nur für die Durchführung des Rechnungsdruckes benötigt. Sie wird durch ein Makro gefüllt und nicht irgendwie durch Formulare bearbeitet.

In der Tabelle "Rechnungsinhalt" werden die folgenden Felder definiert:

Feldname

Feldtyp

Beschreibung

ID

Integer

Die ID ist Primärschlüssel dieser Tabelle. Die ID ist identisch mit der der Tabelle "Rechnung". Eine Beziehung wird hier aber nicht festgelegt.

Rechnungsinhalt

Text

Länge des Textes sollte groß gewählt werden, da hier für eine der Serienbrieffunktionen alle Rechnungsinhalte in einem Datensatz gespeichert werden. Im Beispiel ist der Länge auf 10000 Zeichen festgelegt.

In der Tabelle "Verkauf" werden die folgenden Felder definiert:

Feldname

Feldtyp

Beschreibung

ID

Integer

Die ID ist Primärschlüssel dieser Tabelle. Sie ist als Autowert festgelegt.

Anzahl

Tiny Integer

Je größer die Anzahl der Waren sein sollte, desto größer ist natürlich der Umfang dieses Feldes festzulegen. Mit Tiny Integer geht die Anzahl von -128 bis +127 – für ein einfaches Beispiel völlig ausreichend.

Waren_ID

Integer

Hier wird die "ID" der Tabelle "Waren" als Fremdschlüssel gespeichert.

Rechnung_ID

Integer

Hier wird die "ID" der Tabelle "Rechnung" als Fremdschlüssel gespeichert.

In der Tabelle "Waren" werden die folgenden Felder definiert:

Feldname

Feldtyp

Beschreibung

ID

Integer

Die ID ist Primärschlüssel dieser Tabelle. Sie ist als Autowert festgelegt.

Ware

Text

 

Preis

Dezimal

Bei der Preisangabe ist darauf zu achten, dass 2 Nachkommastellen vorgesehen sind. Sonst sind nur ganzzahlige Angaben möglich.
Das Feld kann auch entsprechend schon mit der Währungsangabe vorformatiert werden. Die Formatierung wird in Tabellen, Abfragen und beim Druck über einen Serienbrief berücksichtigt.

Die Tabellen sind für die Rechnungserstellung folgendermaßen verbunden:

Die Tabelle Anschrift wird separat bestückt und dient als erstes Beispiel für den Seriendruck. Die Kombination mit der Rechnungserstellung zeigt dann ein erweitertes Anwendungsbeispiel.

Formulare

Die Formulare greifen mit unterschiedlichem Schwierigkeitsgrad auf die Serienbrieffunktion bzw. auf Textfelder eine Vorlage zu. Der einfachste Zugriff erfolgt für den Inhalt, der in der Tabelle "Anschrift" gespeichert wird.

Das Formulare «Anschrift»

Das erste Formular stellt ein einfaches Eingabeformular für Adressen dar.

 

Das Formular speichert die Daten in der Tabelle "Anschrift" ab. Das Primärschlüsselfeld ist farblich abweichend gekennzeichnet, da der Wert automatisch erstellt wird. Das Feld "Geschlecht" wird über ein Listenfeld beschrieben. Der Druck des aktuellen Datensatzes kann direkt über den Writer erfolgen. Über den Button wird hierzu ein Makro ausgeführt. Mehr dazu etwas weiter unten.

 

Der Formularnavigator zeigt keine Besonderheiten auf. Bei einem Formular, das lediglich zum Bestücken einer Tabelle genutzt wird, ist dies auch nicht weiter verwunderlich.

 

Das Listenfeld wird nicht über eine zusätzliche Tabelle per SQL geladen, sondern ist hier direkt über eine Werteliste definiert. Unter Eigenschaften → Daten → Listeninhalt sind die Werte «m» und «w». Um die Werte in dieser Form einzugeben muss lediglich das vorgesehene Feld mit dem Cursor betreten werden. Das Feld klappt auf und über die Eingabe von Strg+Enter kann nach der Eingabe des ersten Wertes in die zweite Zeile für einen zweiten Wert gewechselt werden.

Der Listeninhalt wird in die dem Formular zugrundeliegenden Tabelle "Anschrift" übertragen. Dargestellt werden allerdings die unter Eigenschaften → Daten → Listen-Einträge gemachten Einträge. Hier ist auf die selbe Reihenfolge zu achten, damit nicht bei der Anzeige «weiblich» in der Tabelle «m» abgespeichert wird.

Ausdruck aus dem Formular «Anschrift»

Bevor ein Ausdruck eines Serienbriefes über die Datei «Beispiel_Serienbrief.odt» erfolgen kann, muss die Datenbank in LibreOffice angemeldet werden:

Extras → Optionen → LibreOffice Base → Datenbanken → Neu

Damit das Makro richtig funktioniert, muss für die Datenbank der im Makro vermerkte Name «Beispiel_Datenbank_Serienbrief_direkt» gewählt werden. Datenbank und Textdokument für den Serienbrief müssen außerdem in dem gleichen Verzeichnis liegen.

Wird anschließend in einem Datensatz der Button Druck über Writer betätigt, so wird eine neue Textdatei mit dem im Serienbrief abgelegten Feldinhalt erstellt. Der Dateiname wird aus dem Nachnamen und einer folgenden 0 sowie der Endung «odt» zusammengefügt. Die Datei hat den selben Inhalt, wie ihn auch ein Druck, ausgelöst von der ursprünglichen Serienbriefdatei, hätte.

Der Serienbrief selbst wurde hier einfach über den Datenbankbrowser erstellt:

Mit F4 oder über Ansicht → Datenquelle oder auch über den entsprechenden Button in der Symbolleiste werden die Datenquellen angezeigt. Aus der Datenquelle wird die entsprechende Tabelle (hier «Anschrift») ausgesucht. Die Tabelle wird rechts davon angezeigt. Jetzt können die Serienbrieffelder durch einen Druck mit der linken Maustaste auf den jeweiligen Tabellenkopf in das darunterliegende Writer-Dokument gezogen werden.

Die Feldauswahl der Serienbrieffelder kann alternativ auch über Einfügen → Feldbefehl → Andere → Datenbank → Seriendruckfeld erfolgen.

Statt aber diesen Vorlagenbrief zuerst zu starten, dann den Datensatz auszusuchen, den Druck zu starten und die Bedingungen dafür festzulegen, erfolgt die Erstellung des Zieldokumentes bei Betätigung von Druck über Writer direkt, ohne das Ausgangsdokument auf dem Bildschirm zu öffnen.

Dieser Zugriff auf das Dokument wird über das Makro «Serienbrief» erreicht.

Makrosteuerung zum Ausdruck im Serienbrief

SUB Serienbrief

        DIM oForm AS OBJECT

        DIM oDB AS OBJECT

        DIM stDir AS STRING

        DIM loFeldID AS LONG

        DIM loID AS LONG

Der Datenbankwertebereich für "Integer" entspricht dem StarBasic-Wertebereich für "Long". Deshalb müssen Integer-Felder aus der Datenberg mit einer Long-Variablen ausgelesen werden, wenn wirklich alle Werte erfasst werden sollen.

        DIM arProps()

Die nötigen Variablen für den Serienbrief werden in dem Array arProps() gespeichert.

Nach der Deklaration aller Variablen wird aus dem aktuell im Formular angezeigten Datensatz der Primärschlüsselwert ausgelesen. Dadurch wird erreicht, dass tatsächlich nur ein Datensatz anschließend für den Druck weitergegeben wird. Es wird davon ausgegangen, dass das Feld in der Tabelle die Bezeichnung "ID" hat und die Tabelle aktuell im Formular «MainForm» als Datengrundlage dient. Das Feld "ID" muss für dieses Vorgehen nicht im Formular als Formularfeld angezeigt werden. Der Zugriff geht hier direkt über die in der Datengrundlage des Formular vorhandenen Felder.

        oForm = thisComponent.Drawpage.Forms.MainForm

        loFeldID = oForm.findColumn("ID")

        loID = oForm.getLong(loFeldID)

Die Variable «loID» enthält jetzt den aktuellen Primärschlüsselwert.

        MailMerge = CreateUnoService("com.sun.star.text.MailMerge")

        oDB = ThisComponent.Parent

Der Zugriff auf die URL ist nicht vom Formular aus direkt möglich. Es muss auf den darüber liegenden Frame der Datenbank Bezug genommen werden, damit der Pfad ermittelt werden kann, in dem die Datenbank zur Zeit liegt.

        stDir = Left(oDB.Location,Len(oDB.Location)-Len(oDB.Title))

Der Titel der Datenbank wird von der URL abgetrennt. Anschließend werden die verschiedenen Details für den UnoService festgelegt. Dies kann entweder über eine jeweils einzelne Definition wie MailMerge.DataSoureName, MailMerge.DocumentURL usw. oder über die Anweisung WITH MailMerge und der Aufzählung der Detailanweisungen erfolgen.

        WITH MailMerge

                .DataSourceName = "Beispiel_Datenbank_Serienbrief_direkt"

Die Datenbank muss unter diesem Namen eingebunden sein.

                .DocumentURL = stDir+"Beispiel_Serienbrief.odt"

Die Datei «Beispiel_Serienbrief.odt» liegt im gleichem Pfad wie die Datenbank.

                .SaveAsSingleFile = true

Aus dem Inhalt sollen Einzeldokumente erstellt werden, nicht mehrere Briefe hintereinander in einem Dokument. false oder 0 bedeutet «nein», true oder 1 bedeutet «ja».

                .CommandType = 0

Die folgenden Datenquellentypen gibt es: 0 = Tabelle, 1 = Abfrage, 2 = SQL-Code

                .Command = "Anschrift"

Abhängig von dem Typen wird hier entweder die Bezeichnung der Tabelle/Abfrage oder der komplette SQL-Code angegeben.

                .Filter = "ID ="+loID

Der Wert des Primärschlüssels wird hier als Filter für den SQL-Code an den Serienbrief übergeben.

                .FileNameFromColumn = true

Hier wird angegeben, dass der Bezeichner für den Brief aus dem Inhalt eines Tabellenfeldes erstellt werden soll

                .FileNamePrefix = "Nachname"

 Aus dem Inhalt dieses Tabellenfeldes wird der Bezeichner für die Datei übernommen. Dabei wird hinter den Nachnamen eine 0 gesetzt, um gegebenenfalls mehrere Serienbriefe an eine Person mit gleichem Namen zu senden. Der folgende Brief erhält dann eine 1 usw.

                .OutputType = 2

Der Serienbrief kann direkt an den Drucker gehen (Drucker = 1), er kann als Datei abgespeichert (Datei = 2) oder als E-Mail versandt werden (Mail = 3).

                .OutputUrl = stDir

Die zu erstellende Datei wird hier im selben Pfad abgelegt, in dem sich auch die Datenbank befindet. Hier könnte natürlich jeder beliebige Pfad eingegeben werden.

        END WITH

        MailMerge.execute(arProps())

END SUB

Das Formular «Anschrift_Textfelder»

Vom Aufbau her unterscheidet sich das Formular «Anschrift_Textfelder» nicht vom Formular «Anschrift». Allerdings ist in den Formular-Eigenschaften unter Daten → Art des Dateninhaltes «Abfrage» ausgewählt. Dies ist nur notwendig, da in den Adressen eine Anrede dargestellt werden soll.

Die Abfrage, auf der das Formular beruht, zeigt die gesamte Tabelle "Anschrift" auf:

SELECT

 "ID", "Vorname", "Nachname", "Geschlecht" AS "Geschl_kurz",

 "Straße_Nr", "Postleitzahl", "Ort",

 CASEWHEN( "Geschlecht" = 'm', 'Herrn', 'Frau' ) AS "Geschlecht"

 FROM "Anschrift"

Das ursprüngliche Feld "Geschlecht" wird mit einem Alias versehen, da die selbe Bezeichnung jetzt für die Anrede genutzt werden soll. Mit der Funktion CASEWHEN wird ausgelesen, ob in dem Feld "Geschlecht" der Wert «m» steht. Steht dort dieser Wert, so wird «Herrn» ausgegeben. Ansonsten erscheint «Frau».

Ausdruck aus dem Formular «Anschrift_Textfelder»

Das Formular «Anschrift_Textfelder» arbeitet nicht mit der Serienbrieffunktion von LibreOffice zusammen. Stattdessen wird auf Platzhalter zugegriffen.

Zuerst muss im Writer eine Vorlage mit Platzhaltern erstellt werden. Dies geschieht über Einfügen → Feldbefehl → Funktionen → Platzhalter. Die Platzhalter werden der Einfachheit halber so benannt, wie das entsprechende Feld in der Tabelle bzw. in der Abfrage heißt, deren Inhalt der Platzhalter annehmen soll.