Hi,
ich hab ein recht kompliziertes Problem mit Crystal Reports und Delphi.
Wir haben eine recht große Applikation in Delphi (
Win32) die intern die Zeos Komponenten benutzt. Sinn der Sache ist der, dass man momentan nur eine embedded Firebird Datenbank einbindet (für Einzeplplatz-Versionen) oder einen Firebird-Server (für Netzwerk-Versionen mit mehreren Clients). Später sollen dann
MSSQL und Oracle dazukommen. D.h. Die Application ist so ausgelegt, dass man im Prinzip jede Datenbank, die Zeos untersüttzt, auch anbinden kann.
Im Programm drin gibt es ein Benutzersystem, hunderten von Zugriffsrechen usw. auf bestimmte Teile des Programms und damit auch auf die Datenbestände/Tabellen an sich.
Jetzt sollen mit Crystal Reports diese Daten ausgewertet werden. Das sind ca. 200 Auswertungen und Statistiken, die ich schon alle erstellt und die auch schon mit dem Programm zusammen ausgeliefert werden.
Dazu hab ich die Delphi Komponente für CRXI verwendet, da gibts TCrpe und TCrpeDS.
Und zwar hat man bei CR ja die Möglichkeit, über integrierte Treiber oder über
ODBC, JDBC usw. eine Datenbank anzubinden. D.h. Crystal kann in dem Fall direkt mit der verbunden Datenbank über
SQL kommunizieren.
In meinem Fall hab ich ja aber keine Ahnung welche Datenbank angebunden ist, zumal noch die Zugriffsgeschichte reinfällt.
D.h. ich hab für jede Tabelle ein *.ttx File angelegt, das die Feldgröße, Feldname usw. beinhaltet. Diese Dateien kann man als eine Felddefinitionsdatei in einen Report laden und kann damit auch einen kompletten Report erstellen.
Erst wenn der Report in einem Programm per TCrpe Komponente geladen wird, wird dynamisch die Datenquelle zugewiesen.
Dazu stellt die Crpe Komponente alle Tabellen, die in dem Report vorkommen, zu Verfügung, dann kann man einfach ein vorhandenes TDataSet damit per DataPointer damit verbinden.
Also hab ich z.B. eine Tabelle MY_DATA in dem Report defniert, muss ich zuerst eine ZQuery erstellen, ein "SELECT * FROM MY_DATA"; ausführen und das daraus resultierende Dataset in ein TCrpeDS Object packen. Dieses repräsentiert sozusagen die Datenquelle für den Report und diese kann ich nun der Tabelle die mir TCrpe ausgibt, zuweisen (tableByName('MY_DATA').DataPointer := myTcrpeDSobj).
Somit ist es für mich beim Report-Erstellen und auswerten völlig schnurz, welches
DBMS dahinter steht, die Applikation gibt mir ja immer das passende Dataset zurück.
Manchen wird es schon aufgefallen sein: Wenn ich einen Report erstellen muss, der zwei Tabellen braucht die per INNER JOIN z.B. verknüpft werden bedeutet das in meinem Fall, dass ich zuerst ein SELECT * FROM tbl1 mache und das Dataset zuweise, dann ein SELECT * FROM tbl2 mache und das Dataset wiederum zuweise.
Crystal macht jetzt den INNER JOIN selbst aus den zwei Datasets die es bekommt. Bei 100000 x 10000 Datensätzen dauert das eine halbe Ewigkeit.
Das Problem liegt daran, dass Crystal - wenn es eine
SQL fähige Datenbank hat - direkt einen
SQL INNER JOIN Befehl zusammenbaut und den an die Datenbank schickt. Genau DAS ist hier nicht der Fall - es geht technisch einfach nicht.
Und genau hier muss ich jetzt ansetzen...
Es wird langsam untragbar, das eine Auswertung, die über
SQL eigentlich 2-3 Sekunden braucht, bei unseren Kunden oft über 15-30 Minuten dauert.
Da ich nicht nur richtige Server anbinden muss, sondern auch eine embedded Firebird Datenbank habe, die schon durch das Programm geöffnet ist, fällt ein direkter Zugriff von CRXI auf die Datenbank weg - ganz zu schweigen von den ganzen Zugriffsrechen usw.
Ein Zugriff über diese *.ttx Files ist aber auch nicht tragbar wegen der Geschwindigkeit.
D.h. ich müsste eine Möglichkeit schaffen, dass CRXI direkt seine
SQL Statements an die Applikation senden kann, diese an die Datenbank schickt und das Resultset wieder an Crystal gibt.
Daher wäre meine Idee:
Die Applikation bekommt eine Netzwerkschnittstelle verpasst, so dass ein zusätzlicher Thread auf Port 5678 hört.
Dann schreibe ich einen
ODBC-Treiber, der sich auf diesen Port verbinden kann und entsprechende Statements an die Applikation weitergeben kann.
So kann ich in Crystal diesen
ODBC Treiber einbinden und damit arbeiten,
SQL Statements die CRXI an den
ODBC Treiber sendet, landen über die
TCP Verbindung in dem Thread der Applikation, hier wird das Statement an die Zeos Library weitergegeben und das Resultset über
TCP an den
ODBC Treiber zurückgeben.
Damit hätte ich alle Voraussetzungen:
+ Crystal hat eine (vorgetäuschte)
SQL Datenbank
+ Statt 10 Statements bei 10 Tabellen die verknüpft werden müssen, wird nur noch ein
SQL Statement erzeugt
+ die Datenbank übernimmt dei Datenverknüfung und CRXI muss das nicht mehr selbst machen.
So, zu dieser Vorgehensweise hätte ich ein paar Fragen:
1. Was halten die eingefleischten Datenbanktechniker hier von dieser Idee?
2. Hat jemand einen besseren Vorschlag?
3. Hat jemand mal einen
ODBC Treiber entwickelt? ich hab mich damit mal etwas beschäftigt, sieht recht heftig aus, vor allem weil ich ja noch ein eigenes Protokoll entwickeln muss, das die Kommunikation zwischen
ODBC Treiber und Applikation herstellt.
4. Sind
ODBC Treiber überhaupt zu empfehlen? Irgendwo hab ich gelesen die sind veraltet - aber einen Ersatz hab ich noch nicht gefunden.
5. Angenommen ich habe eine embedded Firebird Datenbank, kann ich maximal eine ZConnection erstellen. ich hab aber die Applikation und meinen zusätlichen Thread für die
TCP Verbindungen der die
SQL Statements an die Datenbank schicken muss - geht das überhaupt? Ich müsste dann eine ZConnection über zwei Threads benutzen, soweit ich weiß sind die Dinger nicht threadsave - muss ich mir hier meine eigenen Locks einbauen?
6. Lässt sich per
ODBC irgendwie die Syntax der generierten
SQL Statements festlegen? Zum Beispiel versteht eine Oracle Datenbank nicht alles was Firebird versteht - und umgekehrt. Wie kann ich Crystal sagen wie das
SQL Statement aufgebaut sein muss? Oder benötige ich da einen Wrapper im der Applikation die mir den
SQL String analysiert und auf die gerade aktive Datenbank umschreibt? -> Gibt es dafür schon Komponenten? die Zeos haben so etwas soweit ich weiß nicht.