# Spalten einer SQL-Anfrage vorhersagen



## TalkmasterGIP (8. Jan 2010)

Hallo alle zusammen und willkommen zu meinem ersten Post! 

Ich arbeite gerade an der Integration von SQL-Datenbanken (mit JDBC) in ein größeres Projekt. Dabei sollen Daten ausgelesen und anschließend mit verschiedenen Algorithmen weiterverarbeitet werden. Die Datenmengen bewegen sich dabei schlimmstenfalls im Terrabyte-Bereich und deswegen ist das Vorgehen nicht so straight-forward, wie es vielleicht bei kleinerem Umfang der Fall wäre.
Konkret heißt das, dass ich das "Aussehen" eines ResultSets (also z.B. die Spaltennamen oder die Typen in den einzelnen Spalten) gerne vorhersagen würde, *bevor* die tatsächliche (eventuell sehr ressourcenintensive) Anfrage gestellt wird.

Die momentan von mir verfolgte Weg besteht darin nach der Verbindungsaufnahme den Produktnamen der Datenbank zu erfragen, um dann abhängig davon die Anfrage so zu verändern, dass das ResultSet nur eine Zeile enthält (z.B. per TOP bei SQL Server oder LIMIT bei MySQL).


```
/* Minimalbeispiel:
 * Dabei sind 'URL', 'benutzer', 'passwort' und anfrage 'jeweils' Strings,
 * die als Parameter übergeben wurden:
 */

Connection verbindung = DriverManager.getConnection(URL, benutzer, passwort);
String dbname = verbindung.getMetaData().getDatabaseProductName();
String neueAnfrage = transformiereAnfrage(anfrage, dbname);
ResultSet ergebnis = statement.executeQuery(neueAnfrage);

/* über die Metadaten von 'ergebnis' kann ich dann alle nötigen Informationen
 * auslesen
 */
```

Trotz längerer Suche ist es mir aber nicht gelungen herauszufinden, was genau die Anfrage in Zeile 7 für die verschiedenen Datenbanken zurück gibt. :bahnhof: Ich vermute aber schon, dass diese Information irgendwo vorhanden ist - kann mir jemand die Augen öffnen?

Ich nehme natürlich auch gerne alternative Vorschläge an, die mein Problem lösen könnten.


----------



## Manuela (9. Jan 2010)

Hallo,

hier habe ich einen Link der Dir weiterhelfen kann

Galileo Computing :: Java ist auch eine Insel – 22.12 Metadaten

Gruß Manuela


----------



## TalkmasterGIP (10. Jan 2010)

Hi Manuela!

Danke für den Link, aber der hilft mir - meiner Meinung nach - nicht weiter. Zu einem ResultSet die Metadaten zu erfragen und auszuwerten ist kein Problem. Im Gegenteil: genau das habe ich vor. Aber *ohne* dass die ganze Anfrage gestellt wird.

*Hintergrund*:
Die durch die Anfrage erhaltenen Daten sollen anschließend mit verschiedenen - und zur Kompilierzeit nicht feststehenden - DataMining-Algorithmen weiterverarbeitet werden. Dabei soll im ersten Schritt geprüft werden, ob das Ergebnis der Anfrage eine gültige Eingabe für den Algorithmus ist, damit nicht eine - unter Umständen komplizierte - Anfrage an eine 2TB große Datenbank gestellt wird, um dann festzustellen, dass das ResultSet keine gültige Eingabe für den folgenden Algorithmus ist.
Ich möchte also diesen Test implementieren.

Meine momentanen Herangehensweise besteht darin, die Anfrage so zu modifizieren, dass das Ergebnis nur einen Datensatz (also eine Zeile) enthält, denn ich hoffe, dass diese Berechnung wesentlich schneller abgeschlossen ist, als die Berechnung der ursprünglichen Anfrage. Dummerweise unterscheiden sich die Befehle zur Limitierung des Ergebnisumfangs von Datenbank zu Datenbank (eben z.B. TOP bei SQL Server und LIMIT bei MySQL) und deswegen möchte ich den Namen der Datenbank erfragen (Zeile 7 im obigen Minimalbeispiel) und dann abhängig davon die Anfrage transformieren (Zeile 8).

Leider weiß ich nicht, was die Methode getDatabaseProductName() für die verschiedenen Datenbanken zurückgibt (konkret: ergibt das bei einer Anfrage an eine MySQL-Datenbank den String "MySQL"?) und genau das ist meine Frage an euch. Da muss es doch eine Übersicht oder sonst zumindest eine Dokumentation in der entsprechenden JDBC-Treiberumsetzung für die verschiedenen Datenbanken geben! Aber z.B. bei SQL Server sieht diese so aus: getDatabaseProductName Method (SQLServerDatabaseMetaData)
Das hilft mir auch nicht weiter...


----------



## madboy (10. Jan 2010)

Ich würde den DB-Typ nicht aus den Metadaten holen. Klingt mir zu "unsicher". Wenn eine neue DB-Version einen anderen String zurück liefert musst du jedes Mal den Code anpassen.
Vorschlag:
Dort, wo du die Verbindung zur DB erstellst, weißt du schon was für eine DB es sein soll. Abhängig davon würde ich eine Klasse instanziieren, die eine Abfrage erstellen kann, wie du sie brauchst.


```
public interface DbUtil {
public String getLimitQuery(String select, String from, String where);
}

public class MySqlDbUtil implements DbUtil {
public String getLimitQuery(String select, String from, String where) {
   return select + from + where + " LIMIT ...";
}
}

...

statement.executeQuery(dbUtil.getLimitQuery(select, from, where));
```

Wahrscheinlich willst du auch weitere DB spezifischen Queries absetzen. Mit dem Vorschlag hast du dann alle spezifischen SQLs in einer zentralen Klasse und nicht über den kompletten Code verstreut.


----------



## Michael... (10. Jan 2010)

Wenn ich das richtig verstanden habe willst Du an die Metadaten eines ResultSets bevor das Statement ausgeführt wurde.
Man könnte vor der Ausführung des Statements ein Kopie des Statements ausführen in dessen Where Klausel man eine Bedingung ala 
	
	
	
	





```
and 1>1
```
 untergejubelt hat. Liefert eine leere Ergebnismenge aber die Metadaten des ResultSets.


----------



## TalkmasterGIP (10. Jan 2010)

madboy hat gesagt.:


> Ich würde den DB-Typ nicht aus den Metadaten holen. Klingt mir zu "unsicher". Wenn eine neue DB-Version einen anderen String zurück liefert musst du jedes Mal den Code anpassen.


Das stimmt wohl.


madboy hat gesagt.:


> Dort, wo du die Verbindung zur DB erstellst, weißt du schon was für eine DB es sein soll.


Mittlerweile ermittelt der DriverManager selber, auf welche Art Datenbank die URL verweist und wählt dann den passenden Treiber - im obigen Code weiß ich also nicht, mit welcher Datenbank ich verbunden bin.




Michael... hat gesagt.:


> Man könnte vor der Ausführung des Statements ein Kopie des Statements ausführen in dessen Where Klausel man eine Bedingung ala
> 
> 
> 
> ...


Darauf hätte ich auch selber kommen können/sollen. Danke, Michael!


----------



## DerEisteeTrinker (12. Jan 2010)

Probier es mal mit einem AbstractFactory-Pattern in Verbindung mit DAOs, da kannst das gut automatisieren, wenn du bei der Verbindungsaufnahme weißt, welche Datenbank verbunden wird.


----------

