# aus einer DB auslesen



## PollerJava (9. Aug 2007)

Hallo,

ich stelle Diagramme auf einem Panel dar (jfreechart),

Dabei geheh ich so vor, dass, wenn ich eine Linie anzeigen will, von einer Datenbank auslese,
Ändert sich etwas (z.B.: wenn ich nach links gehe um ältere Werte anzuzeigen) lese ich die aktuellen Daten immer aus der DB aus,

Das Auslesen geht so: ich hole mir das ResultSet aus der DB (Tabellen haben immer 2 Spalten -> Uhrzeit| Wert) und speichere dies in einer HashMap<String, Integer>,


Mein Problem ist jetzt, dass ich so viele Daten in der DB habe, dass das auslesen aus der DB sehr lange dauert (z.B.: wenn ich 5 stunden darstelle dann habe ich 6000 Werte, da ich alle 3 Sekunden aufzeichne),

Meine Frage wäre jetzt, wo ich optimieren könnte,

-> das Auslesen dauert ja seine Zeit
-> beim Speichern in die HashMap? (gibts da eine schnellere Möglichkeit)
-> das Erzeugen der series für den JFreeChar kann ich ja auch nicht unbedingt beeinflussen????


Wäre sehr dankbar für TuningTipps!!

lg


----------



## The_S (9. Aug 2007)

Zuerst einmal musst du analysieren was am längsten dauert. Dort gibt es erfahrungsgemäß auch das größte Optimierungspotential. Generell kann man bei Datenbank-Abfragen recht viel falsch/schneller machen. Werden alle Daten immer benötigt? Evtl. Daten erst laden, wenn sie auch wirklich benötigt werden ...

[edit] btw: sowas gehört in die Kategorie Performance


----------



## PollerJava (9. Aug 2007)

naja meine Abfrage geht so:


```
rs = stmt.executeQuery("SELECT * FROM " + dBName + " WHERE zeitstempel >= '" + beginn + "'" + " AND zeitstempel <= '" + ende + "'");
```


Ich hole mir eh nur die Daten, die der User haben möchte aber wenn er mehrere Stunden sehen will, dann dauerts eben,

Ich habe ja in der DatenBank grundsätzlich die Werte als FLOAT gespeichert aber bei den meisten Tabellen wird keine Kommastelle benötigt nur bei einer und die benötigt besonders lange zum laden,

Kann das an der belegten Kommastelle liegen?

lg


----------



## tuxedo (9. Aug 2007)

Versuch das ganze doch mal zu debuggen und schau wo er so lange braucht. Dann siehst du genau wo du optimieren musst. Das Rätsel-Raten ist ja nur suboptimal.

- Alex


----------



## PollerJava (9. Aug 2007)

Ich hab ihn, diesen Zeitfresser:

ich habe eine HashMap:


```
HashMap<String, Float> dBWerte
```

und benötige aus dieser alle FloatWerte:


```
Object[] values =  dBWerte.values().toArray();
```

jetzt hab ich diese als Objects da, benötige aber ein Array mit Double- Werten oder im "Number value" - Format,
daher mach ich folgendes beim for- Schleifen- Durchlauf:



```
series.add(...,  Double.parseDouble(values[i].toString());
```


Wie könnte ich das jetzt schneller machen?

Ich habs schon so gemacht 


```
Double[] values =  (Double[])dBWerte.values().toArray();
```

aber da bekomme ich eine ClassCastException,

Bin sehr dankbar für jede millisekunde!!


lg und vielen Dank


----------



## bygones (9. Aug 2007)

warum der cast in einen array ueberhaupt ?

hol dir doch einfach das ganze als collection... dann haste auch als Float

ansonsten musst du immer noch bei toArray() folgendes machen

```
Double[] values =  (Double[])dBWerte.values().toArray(new Double[0]);
```


----------



## PollerJava (9. Aug 2007)

und wie würdet ihr das machen?

ich habs jetzt so:


```
private XYDataset createDataset(String name, RegularTimePeriod start, final HashMap<String, Float> dBWerte, final int trendCnt, final int xSeite)                                 // erstellt das dataset
        {
        series[xSeite][trendCnt] = new TimeSeries(name, start.getClass()); 
        Double[] values =  (Double[])dBWerte.values().toArray(new Double[0]);
        String[] times = dBWerte.keySet().toArray(new String[0]);
        for (int i = 0; i < dBWerte.size(); i++) 
            {
            dateTime.setDateTime(times[i]);  // hier hole ich mir aus den String die Sec, Min, Stunden usw heraus                          
            series[xSeite][trendCnt].add(new Second(dateTime.getSecond(), dateTime.getMinute(), dateTime.getHour(), dateTime.getDay(), dateTime.getMonth(), dateTime.getYear()), values[i]);                  
            }  
        dataset = new TimeSeriesCollection();   
        dataset.addSeries(series[xSeite][trendCnt]); 
        return dataset;
        }
```


bin eh schon von 11Sekunden!! auf 340ms heruntergekommen aber je schneller desto besser (zumindest bei Programmen)

lg


----------



## The_S (10. Aug 2007)

Zu deinem Statement (auch wenn da nicht mehr viel geht):

Nie "Select *" schreiben. Auch wenn du alle Spalte benötigst, schreib den Spaltennamen dazu. Ist immer schneller. Und anstelle von "zeitstempel >= beginn and zeitstempel <= ende" schreibste lieber "zeitstempel BETWEEN beginn AND ende". Dafür gibts diese Funktion ja  .


----------



## PollerJava (10. Aug 2007)

Hat jemand eine Idee, warum ich bei dieser Zeile:


```
rs = stmt.executeQuery("SELECT zeitstempel  FROM " + dBName + " WHERE zeitstempel BETWEEN '" + tsAktStart + "'" + " AND '" + tsAktEnd + "'");
```

diesen Fehler bekomme:


```
Fehler beim Auslesen aus der DB: org.firebirdsql.jdbc.FBSQLException: Invalid column index.
```


obwohl, wenn ich diese Zeile oben in der DosBox eingebe, funktionierts einwandfrei, mit 

```
rs = stmt.executeQuery("SELECT * FROM " + dBName + " WHERE zeitstempel BETWEEN '" + tsAktStart + "'" + " AND '" + tsAktEnd + "'");
```

funktionierts auch im Programm einwandfrei,


lg


----------



## The_S (10. Aug 2007)

Sicher dass der Fehler beim Ausführen auftritt und nicht evtl. beim Auslesen des ResultSets?


----------



## PollerJava (10. Aug 2007)

Nö, stimmt, hier liegt der Fehler:



```
while(rs.next())
                {
                dBWerte.put(rs.getString(1), rs.getFloat(2));
                }
```


FehlerMeldung:


```
Fehler: org.firebirdsql.jdbc.FBSQLException: Invalid column index.
```


wie kanns das geben?? Ich müsste ja das gleiche rausbekommen wie bei SELECT * FROM oder??


AHH ich weiß schon, ich muss ja beide Spaltennamen angeben!! Alles kalr, 

vielen Dank für die hilfe


----------



## The_S (10. Aug 2007)

PollerJava hat gesagt.:
			
		

> AHH ich weiß schon, ich muss ja beide Spaltennamen angeben!! Alles kalr,
> 
> vielen Dank für die hilfe



So ist es  . Und als kleiner Tipp: Sprech die Spalten nicht über den index, sondern direkt über den Spaltennamen an. Also statt


```
String str = rs.getString(1);
```

lieber


```
String str = rs.getString("zeitstempel");
```

Hat folgende Gründe:

- Hast du mal einen "etwas" längeren SQL verlierst du nicht mehr so leicht den Überblick
- Jemand, der dein Programm warten muss und sich nicht damit auskennt, versteht schneller was du machst. Mit rs.getString(1) kann halt keiner so viel Anfangen wie mit rs.getString("zeitstempel");  .


----------



## PollerJava (10. Aug 2007)

Hallo,

ich lese aus einer Datenbank folgendermaßen aus und speichere die Abfragen in einer HashMap dBWerte:


```
public static ResultSet rs;

rs = stmt.executeQuery("SELECT zeitstempel, wert FROM " + dBName + " WHERE zeitstempel BETWEEN '" + tsAktStart + "'" + " AND '" + tsAktEnd + "'"); 

try {
              while(rs.next())
                    {               
                    dBWerte.put(rs.getString("zeitstempel"), rs.getFloat("wert"));
                    }
                }
            catch (Exception e)
                {
                System.out.println("Fehler Klasse DBReader-------->: " + e);
                }
```

ab und zu bekomme ich folgende Fehlermeldung und die Linie (auf meinem jFreeChart werden die Werte angezeigt) wird natürlich nicht angezeigt:


```
Fehler Klasse DBReader-------->: org.firebirdsql.jdbc.FBSQLException: The resultSet is closed
```


Ich weiß jetzt nicht, warum der resultSet zumacht? wenn ich beim nächsten mal abfrage, gehts wieder ganz normal?

Weiß da jemand ein Lösung,

lg


----------



## PollerJava (10. Aug 2007)

Hat vielleicht zum oberen Problem eine Idee oder Erfahrungen damit,

ich kann nichts findem im Netz,

vielen Dank


----------



## PollerJava (13. Aug 2007)

leider hänge ich an dem Problem fest,

hat damit wirklich keiner Erfahrungen??

lg


----------



## The_S (13. Aug 2007)

Kannst du den Fehler rekonstruieren? Evtl. irgendwo ein if, der das ResultSet schließt, bevor alle Daten ausgelesen sind? Oder alternativ: werden irgendwo noch Daten versehentlich ausgelesen während das rs schon zu ist?


----------



## PollerJava (13. Aug 2007)

Kann es sein, dass der Fehler deshalb vorkommt, da ich in die Datenbank schreibe während ich auslese und deshalb diesen Fehler bekomme,

Ich kann den Fehler nicht konstruieren,
Ich schließe den ResultSte weder noch mache ich sonst was mit ihm, er soll mir nur die Zeilen aus der DB geben und aus,


Hier ist meine Methode:


```
public void findFile(String name, int zeitRaum, Date beginn, Date ende, Date zeitVon, Date zeitBis, int xSeite)                // zeitRaum ist Jahr, Monat, Tag, Stunde, ... in der JComboBox, beginn ist das Datum aus der linken, oberen JComboBox 
        {
        try {          
            dBName = searchDBName.getDBName(name, xSeite);
            tsBeginn = new Timestamp(beginn.getTime());
            tsEnde = new Timestamp(ende.getTime());                                       
            switch(zeitRaum)
                {
                case 1 bis 5 break
                case 6: rs = stmt.executeQuery("SELECT zeitstempel, wert FROM " + dBName + " WHERE zeitstempel BETWEEN '" + zeitVon + "'" + " AND '" + zeitBis + "'");
                        break;   // bei case 6 liegt der Fehler 
                default:System.out.println("Klasse DBDriver bei default!");
                }                               
            try {
                while(rs.next())
                    {               
                    dBWerte.put(rs.getString("zeitstempel"), rs.getFloat("wert"));
                    }
                }
            catch (Exception e)
                {
                System.out.println("Fehler Klasse JDBDriver-------->: " + e);   // da bei case 6 der Fehler liegt, bekomme ich diese Exception (manchmal aber zu oft)
                }
            XYTrend.updatePanel(dBWerte, name, xSeite, einheit, lineColor, zeitRaum);
            dBWerte.clear();
            }
        catch (Exception e) {System.out.println("Fehler in der Klasse DBDriver beim Auslesen aus der DB: " + e );}
        }
```


----------



## The_S (13. Aug 2007)

Was ist denn dbWerte für eine Klasse? Kommentiere entsprechende Zeilen doch einfahc mal aus und überprüfe ob der Fehler dann immernoch auftritt. Oder bau ein zusätzliches sysout ein oder lass den debugger mitlaufen. Dann siehst du genau woran es liegt!


----------



## PollerJava (13. Aug 2007)

dbWerte ist eine HashMap<String, Float>


----------



## The_S (13. Aug 2007)

Wo schreibst du denn dann wieder in die DB? Kann die Zeile nicht finden.


----------



## Guest (13. Aug 2007)

Das "public static" ist auch uncool und kann die Ursache vieler deiner Probleme sein. Halte ResultSets nur so lange, bis du sie ausgelesen hast.
	
	
	
	





```
public static ResultSet rs;
```
Vor allem, wenn das dazugehörige Stamtement eine Methodenvariable ist, wird es nach dem Verlassen der Methode verworfen und somit 
wird auch das ResultSet geschlossen.

Trenne auch die GUI-Logik von der Persistenzschicht.


----------



## PollerJava (13. Aug 2007)

bei mir sind beide als KlassenVariable deklariert und somit können sie ja nicht verworfen werden oder??

lg und vielen Dank


----------



## PollerJava (13. Aug 2007)

Also wenn den Methodenaufruf "fillTable" auskommentiere, dann funkionierts einwandfrei,


```
public void run()                                                           // diese run()-Methode wird von einem timer alle 5 sec aufgerufen
        {
        fillTable(name, new Timestamp(System.currentTimeMillis()), (Arr1[arrayIndex]);
        }


  protected void fillTable(String tableName, Timestamp zeitStempel, float wert)
            {
            try {
       /* # */         DBDriver.stmt.executeUpdate("INSERT INTO " + tableName + " (zeitstempel, wert) VALUES ('" + zeitStempel + "', "  + wert + ")");
                }
            catch (SQLException e) 
                {
                System.out.println("Fehler Klasse JankDBWriter beim Schreiben in die Tabelle " + tableName + " : " + e);
                }
            }
```

Ich schätze mal, dass ich in dieser Zeile (#) in die Datenbank hineinschreibe und dann (wie Gast geschrieben hat) ResultSet zurzeitig geschlossen wird und ich daher die Exception in der anderen Klasse bekomme,

jetzt weiß ich aber nicht, wie ich das beheben könnte??

2 ResultSets und 2 Statements??? 

Wäre sehr dankber für weitere Hilfe und vielen dank an HIB für die Geduld


----------



## The_S (13. Aug 2007)

Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Kommentiere entsprechende Zeilen doch einfahc mal aus und überprüfe ob der Fehler dann immernoch auftritt. Oder bau ein zusätzliches sysout ein oder lass den debugger mitlaufen. Dann siehst du genau woran es liegt!


----------



## PollerJava (13. Aug 2007)

das hab ich schon gemacht, aber ich kanns einfach nicht nachvollziehen, 

wenn ichs debugge, bekomme ich bei jeden durchlauf eine Exception,

keine Ahnung, was ich da noch machen kann,


----------



## The_S (13. Aug 2007)

Und WO bekommst du die Exception? Wie schaut die Umgebung aus?


----------



## PollerJava (13. Aug 2007)

genau hier (das ist die while- Schleife aus der Methode "findFile":


```
while(rs.next())
                    {               
                    dBWerte.put(rs.getString("zeitstempel"), rs.getFloat("wert"));
                    }
```


Meine Vermutung ist folgende:

Ich lese mit 


```
rs = stmt.executeQuery("SELECT zeitstempel, wert FROM " + dBName + " WHERE zeitstempel BETWEEN '" + zeitVon + "'" + " AND '" + zeitBis + "'");
```
in der Methode "findFile" aus der DB aus aber währenddessen schreibe ich auch mit der Methode fillTable in die DB und es wird nach dem schreiben in die DB mein ResultSet geschlossen und deshalb kann ich nicht fertig auslesen und bekomme eben die Exception.

Kann das so sein und wenn ja , was kann ich da machen???



```
protected void fillTable(String tableName, Timestamp zeitStempel, float wert)
            {
            try {
                JankDBDriver.stmt.executeUpdate("INSERT INTO " + tableName + " (zeitstempel, wert) VALUES ('" + zeitStempel + "', "  + wert + ")");
                }
            catch (SQLException e) 
                {
                System.out.println("Fehler Klasse JankDBWriter beim Schreiben in die Tabelle " + tableName + " : " + e);
                }
            }
```


----------



## Guest (13. Aug 2007)

Kann es sein, dass du ein und das gleiche Statement für beide Operationen verwendest?
Sowas geht nicht. Mach die ganzen Static sachen weg und definiere sie einfach in den Methoden,
wo sie verwendet werden.


```
PreparedStatement stmt = null;
try
{
   stmt = getConnection().prepareStatement(String.format("SELECT zeitstempel, wert FROM %s WHERE zeitstempel BETWEEN ? AND ?", dBName));
   stmt.setTimstamp(1, new Timestamp(zeitVon.getTime()));
   stmt.setTimstamp(2, new Timestamp(zeitBis.getTime()));
   ResultSet rs = stmt.executeQuery();
   
   ...

   rs.close();
}
catch(SQLException e)
{
   ...
}
finally
{
   if( stmt != null )
   {
      stmt.close();
   }
}
```
Gleiches für's Update. Nicht das gleiche Statement für beide verwenden.
Eigentlich gehört die Connection auch geschlossen.


----------



## PollerJava (13. Aug 2007)

Vielen vielen Dank!!!

so gehts jetzt,


lg


----------

