Datenabfrage zu langsam

Hallo,
ich habe schon solche ähnlichen Threads hier gefunden, aber keiner der meinem Problem Entspricht, deshalb mache ich einen neuen auf.
also mein Problem ist:
Ich habe ein Programm in Delphi geschrieben, was auf eine Postgres Datenbank zugreift.
Wenn ich jetzt über das Internet auf die Datenbank zugreife, und ein Paar SQL.Querys ausführe, dauert das ewig, bis mein Programm die ganzen Daten geladen hat.
Weiß vielleicht jemand, woran das liegen kann?
Ich habe auch von eine Methode namens “Caching” gehört, aber ich weiß nicht so recht wie das funktioniert.
Danke
Manu

Was exakt dauert:

  • der Verbindungsaufbau? Falls ja, Du greifst vermutlich via ODBC zu. Das lädt erst einmal das DB-Schema, das kann dauern.

  • das Select selbst? Dann schaue Dir an, was EXPLAIN ANALYSE zu dieser Query sagt.

  • das Übertragen des Results einer Query? Dann schaue, was Du da selektierst. Ein SELECT * einer Tabelle, welche BLOB’s enthält vielleicht?


    Also, mehr Details, mehr Antworten.


    Andreas

ok. Ich werde mir dem Problem am besten selber erstmal genauer Bewusst und dann schrieb ich nochmal.
Muss das erstmal schaffen, selber zusammen zu fassen.

Also neuer Versuch.
Gibt es eine Möglichkeit, Daten die ich aus meiner Datenbank hohle zwischen zu Speichern, damit ich die nicht immer wieder neu hohlen muss?
Vielleicht könnt ihr mit einem Beispiel was anfangen.
Ich hohle mir 100 Zeilen dann hohle ich mir danach 100 andere Zeilen. Jetzt will ich aber wieder die 100 ersten Zeilen haben ohne einen SQL ausführen zu müssen.

Gibt es da eine Möglichkeit, bestimmte Zeilen, (wie soll ich sagen) mehr oder weniger in den Zwischenspeicher zu laden, damit ich die mir dann immer wieder hohlen kann, ohne eine neue Abfrage zu machen.

Versteht mann das jetzt vielleicht besser?

Manu

Hi Manu,

bestimmt gibts die, aber
1.) wäre das - so wie du es beschreibst - nicht Sache der Applikation (in deinem Fall Delphi-App)?
2.) bist Du dir sicher, dass eine derartige Zwischenspeicherung erforderlich ist? Eigentlich sind PostgreSQL-Queries recht zügig (auch übers internet, wenn du nicht grad n modem für den Zugang verwendest :slight_smile: )
3.) Schon mal Andreas Kommentare versucht umzusetzen? Queries optimieren, etc pp?

Paste doch mal a bisserl Code (Datenbank und Applikation, Beschreibe Dein Systemumfeld, etc), und verrat doch mal, was seeeehr langsam in sekunden bedeuten könnte

Grüße

Jochen

Ja, nein.

Du kannst einen CURSOR nutzen, damit ersparst Du Dir das mehrfache SELECT. Die Frage ist aber, was lahm ist. Ist es das SELECT an sich oder der Transport der Daten via ODBC vom Server zum Client.

Wie lange das SELECT braucht, und warum es u.U. lange braucht, erklärt Dir die Ausgabe von EXPLAIN ANALYSE. Falls es das SELECT ist, was Zeit verbraucht, könnte ein CURSOR helfen.

Ist es aber die eigentliche Übertragung der Daten zum Client, dann wäre natürlich eine Clientseitige Speicherung nützlich. Das ist dann aber Sache Deiner Applikation.

Ich kann Deine Applikation nicht beurteilen, das ist nun Dein Job.


Andreas

Cache die Daten doch in der Anwendung. Wieviele sind das denn? Gegebenenfalls kannst du sie zur Bearbeitung auch in der Datenbank sperren.

Gruß Uwe

Nun ja. Ich vermute ja, daß es da um ein Blättern im Result von ‘select * from …’ geht und ein Sperren der Tabelle(n) bringt dann vermutlich mehr Frust als Lust an PG. Ist ja für ein ‘anguggen’ auch nicht nötig.


Andreas

Moin,

ok, also meine App, ist ein Kalender, der Termine/Ereignisse in eine Datenbank Speichert.
Der Kalender ist für mehrer Personen Gemacht, also jeder hat halt ein Login und kann dann seine Termine und evt. die Termine von anderen Anschauen. Und wenn ich jetzt im Kalender eine Woche vor, oder zurückgehe, dann muss der halt die Termine aus der Datenbank laden.

Das Problem daran ist, das ich Termine haben die ein recurrency(Wiederholung) haben und die Speichere ich mit einem Extra Syntax in ein Extra Feld.
Jetzt würde ich gerne die ganzen Zeilen, wo das Feld recurrency nicht null ist einmal auslesen und dann immer im Speicher haben, weil ich die Zeilen wo recurrency nicht null ist sonnst immer wenn ich im Kalender das Datum Ändere wieder mit auslesen muss.

Kapiert mann was ich meine?

PS: also Table sperren würde da dann nciht in Frage kommen oder?

Das hätte ich jetzt auch gemacht, Das Problem ist nur, ich weiß das es sowas wie Cache gibt und was es Grob macht, aber nicht wie ich es genau Benutzen soll, und ob es in meinem Fall wirklich das beste ist. :wink:

Danke schon mal für die ganzen Antworten.
Vielleicht kommt zu dem Post ja auch noch eine. :wink:

Okay.

Wenn man sich den Kalender anschauen will, könnte man ja sagen, es interessiert max. z.B. dieses und das nächste Kalenderjahr. Dann könntest Du für diesen Zeitraum on-the-fly diesen Kalender inklusive der Wiederholungen einen Kalender aufbauen und via CURSOR wochenweise durchgehen. Damit entfällt das möglicherweise zeitaufwändige Berechnen der Termine, es muß nur einmal erfolgen.

Dann könnte man sich ja auch mal anschauen, wie Du diese Wiederholungen berechnest, vielleicht kann man da auch was optimieren.

Andreas

Das klingt mal nicht schelcht.

Ich benutze eine TMS Planner Komponente. Kennt ihr die?
auf jeden Fall, rechnet die Komponente die recurrency selber aus.
Ich kann ja mal den SQL hier Posten:
[php]
SELECT *,
(SELECT zip FROM adress WHERE idadress=id_firma) as zip,
(SELECT location FROM adress WHERE idadress=id_firma) as location
FROM calendar

WHERE

((starttime BETWEEN ‘2007-11-5’ AND ‘2007-11-12’) OR recurrency IS NOT NULL) AND
(
id_calendar IN (SELECT termin FROM userintermin WHERE users=102) OR
(
id_user = 102 AND
(status<>4 OR not erledigt)
)
OR
(
((id_user=100 OR id_user=20…) AND status<>4) AND
(

(status<>2 OR id_user IN (SELECT vonuser FROM privat_rechte WHERE sehen = TRUE and vonuser = id_user AND zuuser =102))
)
)
)

[/php]Das “OR recurrency IS NOT NULL”, würde ich gerne nur einmal machen, damit ich die wo recurrency eben nicht null ist nicht jedesmal neu runterladen muss.

Was ist CURSOE?? das kenne ich auch noch nciht… :confused:

Nein.

auf jeden Fall, rechnet die Komponente die recurrency selber aus.

Aso. Das passiert aber außerhalb der DB.

Ich kann ja mal den SQL hier Posten:

  • paß auf mit Dingen wie ‘2007-01-01’. Das ist so erst einmal ein String. Mit 8.3 fallen wohl einige implizite Casts weg, Dein Code fällt dann möglicherweise auf die Nase. Hänge also einen expliziten Cast dran. (::date)
  • schau, ob da passende Indexe sind. EXPLAIN ANALYSE wird Dir dabei helfen

Das “OR recurrency IS NOT NULL”, würde ich gerne nur einmal machen, damit ich die wo recurrency eben nicht null ist nicht jedesmal neu runterladen muss.

Vielleicht wäre es sinnvoller, dies in einer extra Tabelle zu speichern und das via id_user mit dem Kalender zu verknüpfen. Das ‘is not null’ erzwingt möglicherweise einen seq. scan Deiner Kalendertabelle. Möglicherweise hast Du da noch andere ungünstige Dinge im Tabellendesign.

Was ist CURSOE?? das kenne ich auch noch nciht… > :confused: >

Weiß ich auch nicht. Aber CURSOR ist hier erklärt:

PostgreSQL: Documentation: Manuals: PostgreSQL 8.2: Cursors


Andreas

Ich hab mal a bissl gebastelt, wie man das mit den wiederkehrenden Terminen (aller n Tage, aller n Monate, …) abbilden könnte. Perfekt ist das nicht, insbesondere die Tabelle mit den Terminen ist an sich falsch aufgebaut, weil für neue Intervalltypen neue Spalten angelegt werden müßten. Besser wäre eine Tabelle mit Spalte typ und wert.

test=*# select * from calendar ;
 id | start_datum | n_taeglich | n_monatlich
----+-------------+------------+-------------
  1 | 2007-12-12  |          2 |
  2 | 2007-12-12  |            |           3
  3 | 2006-01-01  |          5 |           1
(3 rows)

Das sind also Startdatum (z.T. sehr alt) und eine Spalte n_täglich. Wenn da was drin steht, dann gilt das ab start_datum aller n_taeglich Tage.

Dann eine kleine Funktion:

[php]
create or replace function kalender(IN s date, IN e date, OUT id int, OUT datum date) returns setof record as $$

declare r record;
r2 record;
sd date;
_s int;
_e int;
tage int;
sql text;
begin
tage:=e-s;
for r in select * from calendar loop
– anzahl der zu überprüfenden Tage
_s := s-r.start_datum;
_e := e-r.start_datum;
if _s < 0 then _s := 0; end if;
if _e < 0 then _e := 0; end if;

–raise notice ‘% %’,_s, _e;
id:=r.id;
if r.n_taeglich is not null then
sql := ‘select '’ || s || ‘'::date + s * ‘‘1day’’::interval as datum from generate_series(0,’ || tage || ‘) s
intersect
select '’ || r.start_datum || ‘'::date + s * ‘‘1day’’::interval as datum
from generate_series(’ || _s || ‘,’ || _e || ‘,’ || r.n_taeglich || ‘) s’;
raise notice ‘%’, sql;
for r2 in execute sql loop
datum:=r2.datum;
return next;
end loop;
end if;
end loop;
end;
$$ language plpgsql;
[/php]


Man muß da etwas basteln, um die Parameter für generate_series() zu ermitteln, man will ja auch nicht unnötig große Zwischentabellen anlegen. Ich berechne einfach die Schnittmenge (INTERSECT) der Tage zwischen den Start- und Endtermin und der Tage, die beginnend mit Starttermin und Wiederholungsrate anfallen.

Zum nachvollziehen, gebe ich die dynamischen SQL-Befehle via RAISE NOTICE aus.
Natürlich fehlen da Dinge wie Gültigkeitsprüfungen der Eingaben etc.
Angewendet sieht das dann so aus:


[php]
test=*# select * from kalender(‘2007-12-01’::date,‘2007-12-31’::date);
NOTICE: select ‘2007-12-01’::date + s * ‘1day’::interval as datum from generate_series(0,30) s
intersect
select ‘2007-12-12’::date + s * ‘1day’::interval as datum
from generate_series(0,19,2) s
NOTICE: select ‘2007-12-01’::date + s * ‘1day’::interval as datum from generate_series(0,30) s
intersect
select ‘2006-01-01’::date + s * ‘1day’::interval as datum
from generate_series(699,729,5) s
id | datum
----±-----------
1 | 2007-12-12
1 | 2007-12-14
1 | 2007-12-16
1 | 2007-12-18
1 | 2007-12-20
1 | 2007-12-22
1 | 2007-12-24
1 | 2007-12-26
1 | 2007-12-28
1 | 2007-12-30
3 | 2007-12-01
3 | 2007-12-06
3 | 2007-12-11
3 | 2007-12-16
3 | 2007-12-21
3 | 2007-12-26
3 | 2007-12-31
(17 rows)
[/php]


Aber Du verwendest ja eh eine externe Klasse, die das macht. Das obige ist quasi nur eine kleine Spielerei meinerseits…


Andreas

in der Funktion ist ein Fehler bei der Berechnung des Anfangstermines für die Intervalltermine. Ich wollt nur darauf hinweisen…

Andreas

Ich werde jetzt mal schauen, wie ich es mache. Mit den CURSOR wird bestimmt was gehen.
Also dann. Ich bedanke ich für die Ganzen Tipps die ich von euch bekommen habe.

@akretschmer
was du da gebastelt hast, hat ja jetzt nicht direkt was mit meinem Problem zu tun, wenn ich recht verstanden habe, was du da machst. :wink:
aber danke für deine mühe.

Manu

Depends. Noch ist nicht klar, wo die Zeit verballert wird.

@akretschmer
was du da gebastelt hast, hat ja jetzt nicht direkt was mit meinem Problem zu tun, wenn ich recht verstanden habe, was du da machst. > :wink:
aber danke für deine mühe.

Manu

Jein. Ich hab nur drüber meditiert, wie man das mit den wiederkehrenden Terminen durch die DB lösen könnte. Du machst es ja eh außerhalb. Was dort wie abläuft weiß ich aber nicht.

Andreas

ja das Stimmt. aber vielleicht habe ich auch zu hohe Ansprüche.

Ich habe außer dem Problem eh noch mehrere Probleme, warum das so langsam ist und das SQL Problem ist wahrscheinlich das was nicht am meisten Zeit frisst, deshalb dachte ich ich kümmere mich erstmal um die anderen Sachen und dann schaue ich nochmal ob ich im SQL was Optimieren werde.

Zur Zeit ist es so, dass die Datenbank für 150 Zeilen ca. 2 sec dauert und das ist ja eigentlich ganz ok…oder???
Probleme wird es erst geben, wenn es dann so an die 1000 werden. :rolleyes:

ich denke das ist einfach zu Zeitaufwändig, für das was ich dann an Zeit reinhohle, ich habe nämlich einen Termin und der muss eingehalten werden. :wink:

Aua.

Ich bin gewohnt, daß Abfragen aus Tabellen mit mit 6-stelliger Anzahl von Zeilen in der Tabelle und 3-stelliger Anzahl Zeilen im Resultat innerhalb einer Zeit im einstelligen Millisekundenbereich dauert…

Erwähnte ich schon mal EXPLAIN ANALYSE?

Andreas

BOOOM dann kenne ich mich wohl mit der dauer von Abfragen noch nicht so recht aus. also ist das ja arsch langsam.

ja schon ca. 10 mal, aber ich weiß damit nicht so recht damit umzugehen. soll ich die abfrage in pgadmin unter Abfragewerkzeug machen, oder wie soll ich da was testen…und wie funzt die genau, ich habe das in der Dokumentation nicht so recht gescheckt.

Ich habe das jetzt mal so gemacht
“EXPLAIN ANALYSE die Abfrage von oben”
da steht dann.

“Seq Scan on calendar (cost=1.24…8818.72 rows=12 width=228) (actual time=0.516…0.516 rows=0 loops=1)”
" Filter: ((((starttime >= ‘2007-11-05 00:00:00’::timestamp without time zone) AND (starttime <= ‘2007-11-12 00:00:00’::timestamp without time zone)) OR (recurrency IS NOT NULL)) AND ((hashed subplan) OR ((id_user = 102) AND ((status <> 4) OR (NOT erledigt))) OR (((id_user = 100) OR (id_user = 20)) AND (status <> 4) AND ((status <> 2) OR (subplan)))))"
" SubPlan"
" → Seq Scan on privat_rechte (cost=0.00…35.50 rows=1 width=4) (actual time=0.009…0.009 rows=0 loops=1)"
" Filter: (sehen AND (vonuser = $2) AND (zuuser = 102))"
" → Seq Scan on userintermin (cost=0.00…1.24 rows=1 width=4) (actual time=0.017…0.017 rows=0 loops=1)"
" Filter: (users = 102)"
" → Index Scan using adress_pkey on adress (cost=0.00…3.83 rows=1 width=14) (never executed)"
" Index Cond: (idadress = $0)"
" → Index Scan using adress_pkey on adress (cost=0.00…3.83 rows=1 width=9) (never executed)"
" Index Cond: (idadress = $0)"
“Total runtime: 0.716 ms”

ich muss dazu sagen, das ich jetzt nicht direkt übers internet auf die datenbank zugreife, die ligt gerade bei mir im haus aufm server und ich greife per OpenVPN-Verbinug zu.