GraphView mit SQL Einträgen füllen

  • Hey alle zusammen :)


    ich will in meiner App einen Graphen anzeigen lassen. Auf der x-Achse solle das Datum stehen, welches ich als eigene Spalte in einer SQL Datenbank gespeichert habe als String und zwar wie folgt: dd.mm.yyy kk:mm:ss
    Dazu habe ich eine Spalte mit Preisen und eine mit Liter wo ich jeweils auch immer Strings drin stehen habe, aber welche alles eigentlich Double Variablen sind. Ich weiß also ganz sicher, dass ich diese einfach mit Double.parsDouble umwandeln kann. Jetzt wird in der DB ja nicht alles nach Datum gespeichert, sondern es kann sein, dass ich unter dem ersten Primärschlüssel (wird in der Datenbank als int unter dem Namen id gespeichert. Ist auf jeden Fall einzigartig wie es ja auch sein soll) das Datum 07.03.2012 ??.??.?? befindet (Uhrzeit ist nicht wichtig) und unter dem zweiten Primärschlüssel das Datum 03.01.2012. Jetzt will ich diese Daten in meinem GraphView an die x-Achse schreiben und dann natürlich dementsprechend auch die Preise und Liter in dem GraphView anzeigen lassen. Das alles mache ich über den Primärschlüssel, da das Datum sich ja auch mal doppeln könnte und ich es dann nur einmal ausgegeben bekomme, so wie es bei DBs üblich ist. Ich habe also versucht einen Array zu erstellen in dem die Primärschlüssel drin gespeichert sind und diesen Array dann nach Datum sortiert. Dies haut aber nicht ganz hin. Ich habe einen Fehler weiß aber nicht wie ich ihn beheben soll. Außerdem dauert meine Methode bei schon etwas größeren Datensätzen sehr sehr lange was natürlich nicht so toll ist. Ich weiß aber nicht wie ich das besser machen soll, also das ich das irgendwas optimiere und somit eine höhere Geschwindigkeit erreiche. Vielleicht könnt ihr mir ja ein paar Tipps geben. Wäre super lieb.


    Hier der Java Quelltext:


    Und hier ist der log.txt vom Laufzeitfehler, wenn ich das Fragment aufrufe, mit zwei Einträgen in der DB. Mit einem Eintrag geht es aber die GraphView zeigt mir dann nichts an.



    Vielen Dank schon mal im Voraus.


    LG
    Marco

  • Der Fehler liegt hier


    03-08 15:56:31.608: E/AndroidRuntime(862): android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=929 (# cursors opened by this proc=929)



    Du hast 929 offene Datenbank zugriffe und beim allokieren von 2MB mehr Speicher scheitert er.

  • Hallo Marco,


    das Thema Datum will ich auch noch mal als Tutorial bauen - gerade im Zusammenspiel mit der SQLiteDB gibt es dort ein paar Schwierigkeiten, weil die DB kein Date-Field unterstützt, dafür sind die Integerfelder groß genug für long-Values.
    KORREKTUR: es gibt in der SQLiteDB Funktionen für Datumsangaben - ich lasse meine Speicherung als INTEGER aber stehen. (Danke @killphil / s.u.)


    Typischer Anfängerfehler: du hast das Datum als String in der Datenbank gespeichert und ärgerst dich jetzt mit den ganzen Datumsangaben rum.


    Zunächst der Hinweis: du brauchst eine neue DB-Version und den onUpgrade der Datenbank um die Inhalte des Datumsfeldes umzustellen - aber dazu später mehr.


    Datum als long speichern:
    Die Klassen Date und Calendar können beide auf eine long-Variable umgerechnet werden: dateObj.getTime() oder calendarObj.getTimeInMillis() - diese Zahl kannst du wunderbar in der DB als INTEGER speichern und sortieren.
    Wenn du deine Daten wieder ausliest, dann musst du mit new Date(longvalue) bzw. calendarObj.setTimeInMillis(longvalue) diese Zahl wieder in deine Objekte schreiben.


    Datumsangaben nutzen:
    Du möchstest eine Grafik darstellen mit Datumsangaben auf der X-Achse - das wird eng, wenn du immer dd.mm.yyyy anzeigen willst. OK - du könntest senkrecht schreiben, aber sinnvoller ist es, nur den dd.mm anzuzeigen. Hängt davon ab, wieviele Einträge du darstellen willst.
    (Nebeninfo: Zeichnen eines guten Koordinatensystems war Teil meiner Diplomarbeit vor 25 Jahren, damals in FORTRAN und so was wie LineGraphView musste ich komplett selber aus Linien und Texten zusammensetzen).


    Primärschlüssel SQLiteDB:
    Der Primärschlüssel deiner DB sollte bitte definiert werden als BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT" - die SQLiteDB mag gerne _id als Primärschlüssel. Danach kannst du dann auch sortieren und vor allem kannst du mit diesem long-Value sehr gut in deinem Programm umgehen - da könntest du Geschwindigkeit gewinnen.


    Für alle, die mal eine Datenbank-Vorlesung gehört haben:
    ja, es gibt andere Verfahren für Primärschlüssel - aber die SQLiteDB ist nur eine einfache Datenbank und sollte nicht mit zu viel Abstraktion gesehen werden - es geht hier ja um den schnellen Einsatz in Android und nicht um ausgefeilte Datenbank-Konzepte. ;)


    Und was ist mit onUpgrade?
    Deine Kunden haben schon Daten gespeichert und möchten die auch gerne behalten - du musst die Umstellung der Datenbank (_id zufügen und füllen, Datumsangaben von TEXT auf INTEGER umstellen) also mit den alten Daten machen.
    Dazu solltest du am besten eine neue Tabelle anlegen und die alte als "Datenmüll" bis zur übernächsten Datenbankversion noch behalten, dann kannst du sie löschen.
    In dem onUpgrade musst du dann die Daten der alten Tabelle auslesen (mit den alten Methoden) und in die neue Tabelle schreiben (mit neuen Methoden, aber_id wird bei INSERT automatisch gesetzt, also nicht in den Values für db.insert() einbauen - einfach weglassen, die _id kommt als Ergebnis vom insert und kann im Datensatz nachgetragen werden).
    Bevor du das aber in onUpgrade alles implementierst, mach dir lieber eine eigene Funktion, die die Daten überträgt. Diese Funktion kannst du dann so lange testen, bis deine Daten korrekt übernommen werden.
    Dann mache die Anpassung für die Release-Version und teste die auch gründlich! Die Kunden werden es dir danken...


    Fehlermeldung mit Cursor:
    Du musst einen Cursor auch wieder mit close() schließen - und wahrscheinlich hast du viel zu viele Cursor geöffnet, normalerweise sollte man den Cursor nur öffnen, Daten übernehmen und dann close.
    Man kann auch direkt auf dem Cursor arbeiten, aber die Zugriffsmethoden für die Felder sind sehr unhandlich, darum lieber in eine eigene Datenstruktur übernehmen (und diese Daten dann auch die ganze Zeit nutzen und eher selten neu laden).
    @killphil: Danke für die Cursor-Fehlermeldung - die hatte ich nicht gefunden - konnte das noch ergänzen. :*


    LG
    Uwe

  • Uwe: Häh moment mal ... SQLite hat doch DATETIME als Datentyp und genau wie MySQL gibt es Tonnen von Datekommandos, womit man rechnen kann
    Ich sag mal nur NOW +9 days ect.
    Klar ist das Gerechne mit Integerwerten einfacher im Programmfluss (da kann man ja wandeln per SImpleDate), aber diese Integerwerte sind unlesbar für einen Menschen und in einer DB kann es ruhig als String stehen und dort sollte man zum Select auch gerne und oft die DATE -Kommandos nutzen.


    liest du hier:


    http://www.sqlite.org/lang_datefunc.html

  • Ups - da hab ich die Doku wohl zu wenig gelesen - hab das DATETIME nicht gefunden... :-[


    NACHTRAG: Jetzt hab ich mir die Doku auch mal angeschaut - die speichern das Datum auch nur als String und machen ein paar kleine Funktionen dazu.
    Da gefällt mir ein long dann doch besser, weil die Umwandlungen entfallen bei der Übernahme in Date/Calendar.


    OK - aber die Probleme bei der Umstellung wird Marco trotzdem lösen müssen, denn einfach so einen TEXT in DATETIME umwandeln geht nicht, das muss erst in ein Date-Objekt rein...

  • Uwe: Kleiner Nachtrag von mir, mit einem Augenzwinkern


    (Klugscheissermodus an)


    Das Problem eines Datums was in int oder long gespeichert ist, du benötigst immer einen Bezugspunkt und eine Wertedefinition (Millisekunden oder Sekunden, Minuten was auch immer). Wie z.B. 1.1.1970 ab diesem Zeitpunkt zaehlt dann dieser Differenzwert. In einem Context wie Java, Unix,Php ect. wurden Vereinbarungen getroffen mit welcher Basis Datehandlingfunktionen arbeiten (aber auch die können sich mal ändern).


    In einer Datenbank bist du aber selbst verantwortlich. Sprich wenn jmd mit der SpracheXYZ diesen Wert in der DB öffnet, hat er einen schönen Longwert, weiß aber nicht wo er den Gegenrechnen soll - deswegen sollte immer ein lesbares Format wie String in einer Datenbank präferiert werden.


    Alte Datenbanken welche Datumswerte als int gespeichert haben z.B. kippen nach 2035 zurück ins Jahr 1970.
    Einem String ist es vollkommen egal ob da 2167 oder 1945 steht. Wie gesagt ich spreche hier nur für den Datenbankcontext, somit bleiben diese halt lesbar.


    (Klugscheissemodus aus)



    lg der phil

  • ich kanns nicht lassen, da mal zurückzuargumentieren... *liebguck*


    Zum Glück ist die SQLiteDB recht tolerant, was den Umfang von INTEGER betrifft - long passt da problemlos rein.


    Und weil ich weiß, dass die interne Darstellung bei Java (und Unix, Perl, C++) immer ein long ist, plädiere ich für INTEGER.


    Außerdem ist die Datenbank von Marco ja nicht für die Öffentlichkeit, oder??


    Sorry - bin halt Ingenieur und darum mehr Praktiker als Theoretiker. *grinz*


    Aber ich muss dir recht geben, wenn ich mit anderen Datenbanken arbeite, wo auch mal andere Leute mit zu tun haben, benutze ich immer die DATETIME-Fields.


    *auchklugscheissenkann* :*

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!