Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi Insert into (https://www.delphipraxis.net/204167-insert-into.html)

Walter Landwehr 1. Mai 2020 07:08

Datenbank: Firebird • Version: 2.5 • Zugriff über: IBO

Insert into
 
Hallo ich habe folgenden Code:
Delphi-Quellcode:
                  while not (PatblattQry.EoF = True) do
                  begin
                      if (PatblattQry.FieldByName('TYP').AsString <> 'Z') then
                      begin
                          InsRechnungsPositionenQry.ParamByName('RECHNUNGNR').AsInteger := RechnungNr;
                          InsRechnungsPositionenQry.ParamByName('PATIENTENNUMMER').AsInteger := PatientQry.FieldByName('PATIENTENNUMMER').AsInteger;
                          InsRechnungsPositionenQry.ParamByName('BEHANDLUNGSDATUM').AsString := PatblattQry.FieldByName('BEHANDLUNGSDATUM').AsString;
                          if (MitarbeiterQry.Locate('ANREDE_NAME',PatblattQry.FieldByName('BEHANDLER').AsString,[]) = True) then begin
                             InsRechnungsPositionenQry.ParamByName('MITARBEITERNUMMER').AsString := MitarbeiterQry.FieldByName('MITARBEITERNUMMER').AsString;
                             InsRechnungsPositionenQry.ParamByName('BEHANDLER').AsString := MitarbeiterQry.FieldByName('ANREDE_NAME').AsString;
                          end;
                          InsRechnungsPositionenQry.ParamByName('GEBUEHNR').Value := PatblattQry.FieldByName('GEBUEHNR').Value;
                          InsRechnungsPositionenQry.ParamByName('CODENR').Value := PatblattQry.FieldByName('CODENR').Value;
                          InsRechnungsPositionenQry.ParamByName('SUCHWORT').Value := PatblattQry.FieldByName('SUCHWORT').Value;
                          InsRechnungsPositionenQry.ParamByName('TEXT').Value := PatblattQry.FieldByName('TEXT').Value;
                          InsRechnungsPositionenQry.ParamByName('GOAENR').Value := PatblattQry.FieldByName('GOAENR').Value;
                          InsRechnungsPositionenQry.ParamByName('MENGE').Value := PatblattQry.FieldByName('MENGE').Value;
                          InsRechnungsPositionenQry.ParamByName('TYP').Value := PatblattQry.FieldByName('TYP').Value;
                          InsRechnungsPositionenQry.ParamByName('FAKTOR').Value := PatblattQry.FieldByName('FAKTOR').Value;
                          InsRechnungsPositionenQry.ParamByName('EINZELPREIS').Value := PatblattQry.FieldByName('EINZELPREIS').Value;
                          if PatblattQry.FieldByName('STEUERCODE').Value = Null then
                            InsRechnungspositionenQry.ParamByName('STEUERCODE').Value := 0
                          else
                            InsRechnungsPositionenQry.ParamByName('STEUERCODE').Value := PatblattQry.FieldByName('STEUERCODE').Value;
                          InsRechnungsPositionenQry.ParamByName('STEUERBETRAG').Value := PatblattQry.FieldByName('STEUERBETRAG').Value;
                          InsRechnungsPositionenQry.ParamByName('STEUERSATZ').Value := PatblattQry.FieldByName('STEUERSATZ').Value;
                          InsRechnungsPositionenQry.ParamByName('GESAMTBETRAG').Value := PatblattQry.FieldByName('GESAMTBETRAG').Value;
                          InsRechnungsPositionenQry.ParamByName('KATEGORIE').Value := PatblattQry.FieldByName('KATEGORIE').Value;
                          InsRechnungsPositionenQry.ParamByName('SPLITTING').Value := PatblattQry.FieldByName('SPLITTING').Value;
                          InsRechnungsPositionenQry.ParamByName('MEDIKAMENT').Value := PatblattQry.FieldByName('MEDIKAMENT').Value;
                          InsRechnungsPositionenQry.ParamByName('GEBUEHRENNR').Value := PatblattQry.FieldByName('GEBUEHRENNR').Value;
                          InsRechnungsPositionenQry.ParamByName('TARIF').Value := PatblattQry.FieldByName('TARIF').Value;
                          if (PatblattQry.FieldByName('TYP').AsString = 'M') then
                             InsRechnungsPositionenQry.ParamByName('Medikament').AsString := 'J'
                          else
                             InsRechnungsPositionenQry.ParamByName('Medikament').AsString := 'N';
                          InsRechnungsPositionenQry.ParamByName('Anlage').asDatetime := Date;
                          InsRechnungsPositionenQry.ParamByName('AnlageBenutzer').asString := Constants.User.UserName;
 ----> dauert 43 Sekunden InsRechnungsPositionenQry.ExecSQL;
                      end;
                      PatblattQry.Next;
                  end;
                  {$ENDREGION}
Dazu folgende SQL Anweisung
Delphi-Quellcode:
Insert into tbl_Rechnungspositionen(
RECHNUNGNR, PATIENTENNUMMER, BEHANDLUNGSDATUM, MITARBEITERNUMMER, BEHANDLER, GEBUEHNR, CODENR,
SUCHWORT, TEXT, GOAENR, MENGE, FAKTOR, EINZELPREIS, STEUERCODE, STEUERBETRAG, STEUERSATZ, GESAMTBETRAG,
KATEGORIE, SPLITTING, MEDIKAMENT, GEBUEHRENNR, TYP, Anlage, AnlageBenutzer, GEHOERTZU, TARIF)
Values(
:RECHNUNGNR, :PATIENTENNUMMER,:BEHANDLUNGSDATUM, :MITARBEITERNUMMER, :BEHANDLER, :GEBUEHNR, :CODENR,
:SUCHWORT, :TEXT, :GOAENR, :MENGE, :FAKTOR, :EINZELPREIS, :STEUERCODE, :STEUERBETRAG, :STEUERSATZ, :GESAMTBETRAG,
:KATEGORIE, :SPLITTING, :MEDIKAMENT, :GEBUEHRENNR, :TYP, :Anlage, :AnlageBenutzer, :GEHOERTZU, :TARIF)
Die Anweisung InsRechnungsPositionenQry.ExecSQL; dauert 43 Sekunden. Viel zu lange.:oops:

In der Tabelle sind etwas über 520000 Datensätze.

Hat jemand eine Idee warum das so lange dauert.

hoika 1. Mai 2020 07:15

AW: Insert into
 
Hallo,
43 sek pro Aufruf oder für die 500.000 Datensätze?

Hängen an den Queries noch was dran (Grid)?
Ist die InsertQuery prepared?
Sind auf der Positionstabelle viele Indizes/Trigger?

schotti65 1. Mai 2020 07:23

AW: Insert into
 
Da können doch zig Anhängigkeiten, Indizes, Trigger, sonstwas auf der Tabelle definiert sein. Ich würd als Erstes mit der kompletten SQL Anweisung die Gegenprobe in einem interaktiven Tool machen. Dann weisst du, ob's an Delphi oder an der DB liegt.

Walter Landwehr 1. Mai 2020 08:06

AW: Insert into
 
An hoika:

Pro Datensatz nicht die 500000
Kein Grid keine weiteren Komponenten.
2 Indizes ; 10 Trigger
prepared := False

An schotti65 :

In IBExpert sehr schnell ca. 1 Sekunde

Neumann 1. Mai 2020 08:24

AW: Insert into
 
Wie sehen denn die Trigger aus, gibt es BeforePost / AfterPost Events?

Normal sollte dieser Vorgang < 50 ms dauern.

Walter Landwehr 1. Mai 2020 08:43

AW: Insert into
 
Liste der Anhänge anzeigen (Anzahl: 1)
Kein Before- oder AfterPost

Im Anhang die Trigger.

jobo 1. Mai 2020 09:02

AW: Insert into
 
Wenn es in einem externen Tool schnell geht- wie erwartet-, scheint es ein Codeproblem zu sein.
Was macht die Schleife, die Du mit gepostet hast?
Ist sichergestellt, dass keine Mehrfachinserts mit (verschluckten) Fehlern auftreten?

jobo 1. Mai 2020 09:12

AW: Insert into
 
Nach Übersicht in den Triggern ist es ja doch wohl etwas komplexer.
Es werden diverse Tabellen aktualisiert, dabei werden "sogar" gruppierende Abfragen gemacht, was jetzt nicht mehr unbedingt ne Frage von Millisekunden ist.
Alle Operationen in den Triggern sind der Existenz passender Indizes unterworfen, wenn es um Mengen jenseits von 100000 geht.

Wenn am Ende noch Rechnungsdaten (Gesamtbeträge) aktualisiert werden, kann ich mir auch gut vorstellen, dass es hier einen Sperrkonflikt gibt (der bei einem "Testins Blaue" durch eine autarke DB Query nicht auftritt).

Walter Landwehr 1. Mai 2020 09:48

AW: Insert into
 
Nur mal so zur Info.

Seit Jahren läuft dies eigentlich ohne Probleme. Am Code ist schon seit Jahren nichts geändert worden. Aber seit 2 - 3 Monaten trat dann plötzlich diese Verzögerung ein. Kann es an Firebird liegen?

p80286 1. Mai 2020 09:58

AW: Insert into
 
Zitat:

Zitat von jobo (Beitrag 1463304)
Ist sichergestellt, dass keine Mehrfachinserts mit (verschluckten) Fehlern auftreten?

Zum einen dies, zum anderen, woher kommt der PK? (kann ich natürlich überlesen haben)

Ich hab so etwas immer so gemacht: (pseudocode)
SQL-Code:
 Insert into table (pk,RechnNr) (newpk,:rechnNr);
Update table set........
where pk=(selekt pk from table where rechnnr=:rechnr)
Etwas umständlich aber wenn Rechnungsnummer eindeutig ist gibt es keine Müllupdates.
ggf sollte natürlich vorher überprüft werden ob die Rechnungsnummer besreits existiert.

Gruß
K-H

p80286 1. Mai 2020 10:00

AW: Insert into
 
Zitat:

Zitat von Walter Landwehr (Beitrag 1463311)
Nur mal so zur Info.

Seit Jahren läuft dies eigentlich ohne Probleme. Am Code ist schon seit Jahren nichts geändert worden. Aber seit 2 - 3 Monaten trat dann plötzlich diese Verzögerung ein. Kann es an Firebird liegen?

Die Vermutung liegt nahe. Ist irgendein Update gelaufen?

Gruß
K-H

jobo 1. Mai 2020 10:38

AW: Insert into
 
Das muss nicht mal ein Update gewesen sein.

"Das skaliert nicht" ist die banale Aussage über Code, der unter Last anders arbeitet als im "idle" Betrieb.

Ein "schlampiges" Select Statement läuft jahrelang gut, bis der Order By Teil so groß ist, dass er nicht mehr ins RAM passt. Dann geht alles auf Dateiebene. Bums. Wenn man dann noch ne klassische HDD am Start hat. Doppelbums.

Ich hab kein Plan von Firebird Optimizer Funktionen, nur der Hinweis: Wenn es einen Optimizer Algorithmus gibt, der ansatzweise dynamisch ist, muss er mit Grenzwerten arbeiten, also Grenzwerten von Row Counts usw. vielleicht sogar mit Statistiken. Und dann gibt es auch diesen Effekt, jahrelang lief alles gut, bis diese eine Grenze überschritten wurde. Dann "denkt sich" der Optimizer was neues aus und Bums.
Ich kenne es hauptsächlich von Oracle. Hier gibt es bspw. eine Funktion, die Optimizer Spielregeln "einzufrieren", um solche Effekte zu vermeiden. Das macht man natürlich nur, wenn man mit dem Betrieb zufrieden ist und Größenordnungen der Datenmengen und sagen wir einen Reifegrad der Anwendung erreicht hat, wo Optimierungen eben mehr Risiken und Aufwand als Nutzen bringen.

Neumann 1. Mai 2020 10:52

AW: Insert into
 
Die Trigger machen das Ganze schon komplex und schwer beurteilbar wie aufwändig die Routinen sind.

Was man immer machen kann:

Alte Firebird Versionen ersetzen 2.5.0.. macht definitiv Probleme, wenn 2.5 dann 2.5.9.

Backup Restore der Datenbank

dataspider 1. Mai 2020 12:04

AW: Insert into
 
1 Sekunde für 1 Insert in IBExpert ist eigentlich auch langsam, aber na ja...
43 Sekunden füe 1 Insert aus dem Code - da stimmt was nicht

Wenn du IBExpert hast, dann geht duch mal ins Databas Monitoring und schau dir vor allem die Oldest Active Transaction etc. an.
Die Werte sollten nah beieinader liegen, eigentlich um 1 differieren.
Da du IBO benutzt, solte es eigentlich OK sein...

Wenn man wissen will, ob die DB einen weg hat, dann Backup und Restore...

Wie sind die Einstellungen in IBO und was verwendest du? (TIBOTransaction oder TIB_Transaction etc., TIBOQuery oder ...)
Am schnellsten ist IMO TIB_DSQL.
Warum durchläufst du in PatblattQry alle Datensatze unf machst dann ein
Code:
if (PatblattQry.FieldByName('TYP').AsString <> 'Z') then
Warum nicht gleich im SQL von PatblattQry ein

Code:
where typ <> 'Z'
Dann kannst du das if weglassen.

IBExpert 1. Mai 2020 19:51

AW: Insert into
 
Zitat:

Zitat von dataspider (Beitrag 1463325)
Wenn du IBExpert hast, dann geht duch mal ins Databas Monitoring und schau dir vor allem die Oldest Active Transaction etc. an.

noch hilfreicher ist es, dafür eine traceapi session zu starten, haben ich in einer der letzten Videos auf www.ibexpert.com/stammtisch geziegt wie das geht

TigerLilly 2. Mai 2020 08:46

AW: Insert into
 
Wie so oft: Du musst schauen, wo die Zeit liegen bleibt.
- nimm mal die trigger weg
- nimm die indices weg
- was sagen die performance counter - platte, speicher, CPU
- schau dir an was firebird wirklich macht (Stichwort trace)

Wie andere auch schon gesagt haben, Datenbanken leben und sollten laufen beobachtet werden. In der Regel kündigen sich so Eskalationen an.

tsteinmaurer 2. Mai 2020 15:41

AW: Insert into
 
Walter,

mit Firebird 2.5 bist du in der glücklichen Lage, die Firebird Trace API zur Verfügung zu haben. D.h. wie schon von anderen erwähnt, eine Trace Session anwerfen und sehen, was deine Client-Anwendung tatsächlich auslöst.

- Ev. Locking
- Ev. Background (Trigger etc.) Overhead, auch abhängig von bestimmten Spaltenwerten, die durch das INSERT gesetzt werden
- Vielleicht passiert irgendwo in IBO im Hintergrund ein FetchAll
- etc.

Solltest Du IBExpert verwenden, dann bist du dort (Trace API) bereits gut aufgehoben. Standalone wäre auch unser FBTraceManager eine Option: https://www.upscene.com/fb_tracemanager/

LG

Walter Landwehr 24. Mai 2020 15:03

AW: Insert into
 
So habe lange herumgetestet aber keine Lösung gefunden. Mach ich einen Insert in einer Tabelle wo nur ca. 34000 Datensätze vorhanden sind, geht es sehr schnell aus der Anwendung heraus.

Meine Frage gibt es einen Experten der mir hier weiterhelfen kann. Natürlich gegen Bezahlung.

hoika 25. Mai 2020 01:04

AW: Insert into
 
Hallo,
schon mal Backup/Restore gemacht?

Walter Landwehr 25. Mai 2020 09:54

AW: Insert into
 
Ja habe ich aber ohne Erfolg. Kann es doch mit den großen Datenmengen in der Tabelle zu tun haben?

Walter Landwehr 31. Aug 2020 11:01

AW: Insert into
 
Ich muss das Thema nochmals aufgreifen. Ich habe jetzt eine StoredProc gemacht leider mit den gleichen Erfolg. Um 10 Datensätze in die Datei zu schreiben werden 72 Sec. benötigt. Viel zu Lange. Wer kann mir natürlich gegen Entgeld weiterhelfen.

jziersch 31. Aug 2020 15:01

AW: Insert into
 
2 Ideen:

- Verwendest Du Start-/EndTransactions um Deine Schleife
- Lösch doch mal die Indices

haentschman 31. Aug 2020 15:10

AW: Insert into
 
Zitat:

Lösch doch mal die Indices
...wenn ich mich recht erinnere, dann waren bei Firebird zusammengesetzte Indizies eher kontraproduktiv.:gruebel:
Zitat:

Um 10 Datensätze in die Datei zu schreiben werden 72 Sec. benötigt
...daß heißt, für ein insert into 7,2 Sekunden? :shock:

Vieleicht ist es auch sowas wie mit ADO? ...aber eher unwahrscheinlich.
https://www.delphipraxis.net/192080-ado-langsam.html

jobo 31. Aug 2020 17:39

AW: Insert into
 
Wie hast Du denn die Procudure aufgebaut?
Die Procedure hat den Sinn, die Trigger (weitgehend) zu ersetzen, also eine dedizierte Insert Proc, die alles regelt, alles bis auf meinetwegen Erzeugung der ID des PK. Jedenfalls würde die Proc naturgemäß eine Aufgabe erledigen, hier insert. Deine Trigger bedienen aber mehrere Aufgaben...
Wie ist das umgesetzt?

Hab mir die Trigger noch mal angeschaut. Diesmal sind mir die POSITION Angaben aufgefallen, die mehrmals mehrdeutig sind. Wie sich fb verhält, wenn POSITION mehrdeutig ist, aber für unterschiedliche Mutation Types, weiß ich nicht. Das bedeutet aber vielleicht, das Feuern der Trigger ist in der Reihenfolge mehr oder weniger undefiniert, also zufällig. In der Praxis wäre das ein möglicher Grenzwertfall. Zufällig bedeutet ja nicht, dass es jedesmal anders läuft, bunt durcheinander (und Du schon nach wenigen Inserts über falsche Ergebnisse gestolpert wärst, weil die innere Abhängigkeit der Insert Logik verletzt wird), sondern immer nach dem gleichen, undefinierten Schema (das sich durch interne Algorithmen ergibt) bis ein Kipppunkt (wegen Laufzeit, ..) erreicht ist und die Trigger in anderer Reihenfolge zünden.

Ggf. könntest du in einem funktionierenden (kleinen) System die Trigger so erweitern, dass sie alle ihren Namen und einen fortlaufenden Sequenzwert in eine separate Tabelle eintragen. Diese (funktionierende) Reihenfolge dann über die POSITION Angabe richtig festnageln und auf das große System übertragen (oder wenigstens vergleichen, wie es sich im großen System verhält).
Vielleicht kann man das aber auch einfacher per Trace API feststellen.

Oder mal die SP zeigen (mit den übrig gebliebenen Triggern)

Den Hinweis zur (expliziten) Start/End Transaction finde ich auch wichtig, bei einem einzelnen Insert oder bei einem Einzelnen Aufruf einer Insert Proc ist das nicht notwendig (und meistens kontraproduktiv), da es implizit DB-seitig erfolgt.

Walter Landwehr 1. Sep 2020 11:01

AW: Insert into
 
Habe mal alle Indexe gelöscht.

Hier mal die Procedure:
Delphi-Quellcode:
SET TERM ^ ;

create or alter procedure TBL_RECHNUNGSPOSITIONEN_INS (
    RECHNUNGSPOSITIONENNR type of column TBL_RECHNUNGSPOSITIONEN.RECHNUNGSPOSITIONENNR,
    RECHNUNGNR type of column TBL_RECHNUNGSPOSITIONEN.RECHNUNGNR,
    PATIENTENNUMMER type of column TBL_RECHNUNGSPOSITIONEN.PATIENTENNUMMER,
    BEHANDLUNGSDATUM type of column TBL_RECHNUNGSPOSITIONEN.BEHANDLUNGSDATUM,
    MITARBEITERNUMMER type of column TBL_RECHNUNGSPOSITIONEN.MITARBEITERNUMMER,
    BEHANDLER type of column TBL_RECHNUNGSPOSITIONEN.BEHANDLER,
    GEBUEHNR type of column TBL_RECHNUNGSPOSITIONEN.GEBUEHNR,
    CODENR type of column TBL_RECHNUNGSPOSITIONEN.CODENR,
    SUCHWORT type of column TBL_RECHNUNGSPOSITIONEN.SUCHWORT,
    TEXT type of column TBL_RECHNUNGSPOSITIONEN.TEXT,
    GOAENR type of column TBL_RECHNUNGSPOSITIONEN.GOAENR,
    MENGE type of column TBL_RECHNUNGSPOSITIONEN.MENGE,
    FAKTOR type of column TBL_RECHNUNGSPOSITIONEN.FAKTOR,
    EINZELPREIS type of column TBL_RECHNUNGSPOSITIONEN.EINZELPREIS,
    STEUERCODE type of column TBL_RECHNUNGSPOSITIONEN.STEUERCODE,
    STEUERBETRAG type of column TBL_RECHNUNGSPOSITIONEN.STEUERBETRAG,
    STEUERSATZ type of column TBL_RECHNUNGSPOSITIONEN.STEUERSATZ,
    GESAMTBETRAG type of column TBL_RECHNUNGSPOSITIONEN.GESAMTBETRAG,
    ANLAGE type of column TBL_RECHNUNGSPOSITIONEN.ANLAGE,
    ANLAGEBENUTZER type of column TBL_RECHNUNGSPOSITIONEN.ANLAGEBENUTZER,
    AENDERUNG type of column TBL_RECHNUNGSPOSITIONEN.AENDERUNG,
    AENDERUNGBENUTZER type of column TBL_RECHNUNGSPOSITIONEN.AENDERUNGBENUTZER,
    MERKER type of column TBL_RECHNUNGSPOSITIONEN.MERKER,
    TIERENR type of column TBL_RECHNUNGSPOSITIONEN.TIERENR,
    KATEGORIE type of column TBL_RECHNUNGSPOSITIONEN.KATEGORIE,
    SPLITTING type of column TBL_RECHNUNGSPOSITIONEN.SPLITTING,
    MEDIKAMENT type of column TBL_RECHNUNGSPOSITIONEN.MEDIKAMENT,
    GEBUEHRENNR type of column TBL_RECHNUNGSPOSITIONEN.GEBUEHRENNR,
    TYP type of column TBL_RECHNUNGSPOSITIONEN.TYP,
    LKZ type of column TBL_RECHNUNGSPOSITIONEN.LKZ,
    BEHANDLERNUMMER type of column TBL_RECHNUNGSPOSITIONEN.BEHANDLERNUMMER,
    GEHOERTZU type of column TBL_RECHNUNGSPOSITIONEN.GEHOERTZU,
    TARIF type of column TBL_RECHNUNGSPOSITIONEN.TARIF,
    EINZELPREISBRUTTO type of column TBL_RECHNUNGSPOSITIONEN.EINZELPREISBRUTTO)
as
begin
  insert into tbl_rechnungspositionen (
    rechnungspositionennr,
    rechnungnr,
    patientennummer,
    behandlungsdatum,
    mitarbeiternummer,
    behandler,
    gebuehnr,
    codenr,
    suchwort,
    text,
    goaenr,
    menge,
    faktor,
    einzelpreis,
    steuercode,
    steuerbetrag,
    steuersatz,
    gesamtbetrag,
    anlage,
    anlagebenutzer,
    aenderung,
    aenderungbenutzer,
    merker,
    tierenr,
    kategorie,
    splitting,
    medikament,
    gebuehrennr,
    typ,
    lkz,
    behandlernummer,
    gehoertzu,
    tarif,
    einzelpreisbrutto)
  values (
    :rechnungspositionennr,
    :rechnungnr,
    :patientennummer,
    :behandlungsdatum,
    :mitarbeiternummer,
    :behandler,
    :gebuehnr,
    :codenr,
    :suchwort,
    :text,
    :goaenr,
    :menge,
    :faktor,
    :einzelpreis,
    :steuercode,
    :steuerbetrag,
    :steuersatz,
    :gesamtbetrag,
    :anlage,
    :anlagebenutzer,
    :aenderung,
    :aenderungbenutzer,
    :merker,
    :tierenr,
    :kategorie,
    :splitting,
    :medikament,
    :gebuehrennr,
    :typ,
    :lkz,
    :behandlernummer,
    :gehoertzu,
    :tarif,
    :einzelpreisbrutto);
end^

SET TERM ; ^

/* Folgende GRANT Anweisungen werden automatisch generiert */

GRANT INSERT ON TBL_RECHNUNGSPOSITIONEN TO PROCEDURE TBL_RECHNUNGSPOSITIONEN_INS;

/* Aktuelle Priviligien auf dieser Prozedur */

GRANT EXECUTE ON PROCEDURE TBL_RECHNUNGSPOSITIONEN_INS TO "PUBLIC";
GRANT EXECUTE ON PROCEDURE TBL_RECHNUNGSPOSITIONEN_INS TO SYSDBA;
Ich denke das Liegt an den Triggern. kann mn alles in eine Proc packen inklusive der Schleife?

jobo 1. Sep 2020 11:28

AW: Insert into
 
So nützt die Proc nichts.
Du musst den gesamten Triggercode für das Insert dort aus den Triggern ausbauen (ggf ganze Trigger weglassen) und in die Proc packen.
Die Schleife gehört nicht in die Proc rein.

Die Proc soll das Insert selbst ausführen und alle nachgelagerten Updates und Summenbildung.

Triggercode für update, delete muss erhalten bleiben, solange das nicht ähnlich umgebaut wird.

Also alle betroffenen Trigger disablen und Code "umschichten", in Anführungszeichen, weil es sicher keine 100% Copy/Paste Aktion ist. Mindestens fallen Fallunterscheidungen für den Mutation Typ weg, es geht in der Proc ja nur noch ums Insert.
Außerdem muss Before / After entsprechend in den Flow der Proc einfließen.

Mit disabled Triggern solange die Proc entwickeln/testen, bis ein sauberes insert per Proc gelingt.
Diese Proc kann dann natürlich wie zuvor das Insert in Schleife aufgerufen werden.
Der Test der Proc, also Aufruf und Prüfung des Ergebnis am besten direkt in der SQL Console bis alles stimmt.

Walter Landwehr 1. Sep 2020 12:22

AW: Insert into
 
OK habe ich mir schon gedacht, werde das ganz umbauen und auf die trigger verzichten.

TigerLilly 2. Sep 2020 08:54

AW: Insert into
 
Schau doch, was der Server wirklich macht! Stichwort Trace. Du rätst jetzt herum + weißt nicht, wo du hingreifen sollst.

1) Was macht die Hardware? Rödelt die Platte herum? Kocht der Prozessor?
2) Was macht die Datenbank? Werden triggers ohne Ende gefeuert? Was genau braucht wie lange?

Nur wenn du weißt, was das Problem ist, kannst du es verlässlich lösen + beim nächsten Mal vermeiden!

Delphi.Narium 2. Sep 2020 16:37

AW: Insert into
 
Bei allen SQLs, egal ob Select, Insert, Update, Delete mal den Ausführungsplan anschauen.

Bei Statements, die nur einen Satz "anpacken", sollte ein entsprechender Index genutzt werden. Fehlt der, wird jedesmal die gesamte Datenbank "durchgegraben" (Full Table Scan). Und sowas (schlimmstenfalls mehrfach) in einem Trigger pro Datensatz: Jo, das kann dann schonmal dauern, da wären die von Dir beobachteten Zeiten schon fast kurz zu nennen ;-)

Hat die Tabelle tbl_Artikel einen Index auf ArtikelNr?
Hat die Tabelle tbl_PatDiagnose einen Index auf (ReferenzTabelle, ReferenzNr)?
Hat die Tabelle TBL_PATDIAGNOSE einen Index auf (ReferenzTabelle, ReferenzNr, Dauerdiagnose)?
Hat die Tabelle TBL_PATIENT einen Index auf PATIENTENNUMMER?
Hat die Tabelle TBL_RECHNUNG einen Index auf rechnungNr?

Hat die Tabelle TBL_RECHNUNGSPOSITIONEN einen Index auf (RechnungNr, Steuercode)?
(Laut Createscript von oben nicht, aber pro Insert ein
SQL-Code:
Select sum(Steuerbetrag) from TBL_RECHNUNGSPOSITIONEN where RechnungNr = :RechnungNrInt and Steuercode = 1 Into :Steuer1;
Das schreit so nach 'nem Full Table Scan!!! Oder auch: Hilf mir, ich rödel mir hier 'nen Wolf.)

Das sind jetzt nur mal so "Indexideen". Was Du genau benötigst, was eher kontraproduktiv ist, muss Du wohl oder übel ausprobieren. Jedenfalls gibt es für alles, was ich da vorgeschlagen habe, entsprechende Selects, Updates, ... in den Triggern. Auch wenn die Trigger durch 'ne Prozedur ersetzt werden und sich eventuell die Geschäftslogik dadurch vereinfachen lässt: Solange die Datenbank für viele, einige oder alle Statements 'nen Full Table Scan pro Insert machen muss, wird es nicht wirklich schnell werden.

TigerLilly 3. Sep 2020 06:58

AW: Insert into
 
Das würde ich gern ergänzen: Beim Insert müssen Indices aktualisiert werden, dh da kosten sie eher Zeit. Bei einem Update und Delete helfen sie, den Satz schneller zu finden + bringen eher Zeit.

Aber die Dosis macht das Gift, viele und große und vielleicht sogar redundante Indices kosten uU mehr Zeit als sie bringen.

Delphi.Narium 3. Sep 2020 08:26

AW: Insert into
 
Wenn man große Datenmengen in 'ne Tabelle einfügt, kann es durchaus hilfreich sein, alle für das Insert nicht benötigten Indices zu löschen und nachher neu zu erstellen. Auch wenn die Neuerstellung sicherlich etliches an Zeit benötigt, so kann es duchaus passieren, dass diese Zeit deutlich kürzer ist, als die permanente Indexpflege während der Inserts.

Erfahrungsgemäß reden wir hier nicht von Laufzeitunterschieden im Bereich von Sekunden oder Minuten, sondern eher von Stunden bis Tagen. Sehr grob gesagt: Je größer die Datenmenge insgesamt, um so größer der Laufzeitunterschied.

Jumpy 3. Sep 2020 09:51

AW: Insert into
 
Aber macht das alles den Insert eines Datensatzes so langsam, dass das 72 Sekunden dauert?
Gibt es nicht auch DBs die den Datensatz annehmen und den Index später in ihrer Freizeit aktualisieren?

Delphi.Narium 3. Sep 2020 10:32

AW: Insert into
 
Kommt wohl drauf an, quasi so 'ne Art "Entschiedenes sowohl als auch".

Der Index muss sofort aktuallisiert werden (jedenfalls bei der Vorgehensweise, wie sie hier genutzt wird), damit im Trigger eine entsprechende Abfrage den gerade eingefügten Wert auch wiederfinden kann. Hier wird er sofort für eine Summenbildung in 'ner anderen Tabelle benötigt (select sum(spalte) from tabelle where ..., wobei der gerade eingefügte Satz eine Teilmenge, der durch die Wherebedingung selektierten Sätze, ausmacht). Würde der Index nicht sofort aktuallisiert, hätte es hier im konkreten Fall jedenfalls (mit an Sicherheit grenzender Wahrscheinlichkeit) negative Auswirkungen.

Wie es sich bei 'nem Bulk-Insert verhält, weiß ich nicht, aber das liefe dann vermutlich ohne den Aufruf der Trigger, was wiederum die dort abgebildete Geschäftslogik ruinieren würde.

Und je nach Datenmenge kann ein fehlender Index schon 'ne massive Auswirkung auf die 72 Sekunden haben. Wenn ein Full Table Scan über 'ne Minute zum Summieren von Werten braucht, dann braucht er halt pro Datensatz über 'ne Minute.

Entweder ins Datenbankdesign investieren oder die Hardware massiv aufrüsten, so dass die DB komplett im Arbeitsspeicher landet, die CPU exorbitant schnell ist, Festplatten mit Zugriffszeiten, die gegen 0 tendieren, ...

Bei 'ner handelsüblichen Hardware kann man in Situationen geraten, in denen es sich eben genau so verhält, wie es hier gerade vorliegt.

jobo 3. Sep 2020 17:57

AW: Insert into
 
Es ist ja bekannt, dass das Verhalten plötzlich gekippt ist. Insofern würde ich Fragen nach Indizes usw. nicht im Vordergrund sehen.
Umgekehrt ist das Aufräumen der Trigger und die Umstellung auf eine Prozedur sicher ein recht kleiner Aufwand, denn eigentlich ist alles da, was benötigt wird.
Eine Analyse wie von einigen angesprochen, wäre sicher auch nicht verkehrt, aber es gilt ähnlich wie zuvor, die Zeit kann ich direkt in das Refaktoring stecken.
Und etwas Analyse gibt es ja auch, das Verhalten bei direkter Nutzung einer SQL Console. Hardware Schäden und viele andere Dinge kann man also schon mal ausschließen und es geht nicht vollkommen planlos daher.

Hardwareerneuerung ist ja auch immer ein verlockender Vorschlag und mit dem Monatslohn eines Entwicklers kann man sicher einen guten Server bekommen. Dann muss aber noch die Umstellung gemacht werden und am Ende bleibt die Frage, ob man für den Aufwand einen Faktor 72 oder mehr (grobe Annahme: vorher hat das Insert nur eine Sekunde gedauert) hinbekommt. Mein Tipp wäre eher: nein.

IBExpert 3. Sep 2020 18:25

AW: Insert into
 
Wenn es eine einigermaßen aktuelle Firebird Version ist und man einie einigermaßen aktuelle IBExpert Version haben sollte, dann sollte sich durch das Ansehen dieses Videos https://www.youtube.com/watch?v=MOuCeKTNpOk innerhalb kurzer Zeit mit der Traceapi finden lassen, was der grund ist oder mit dem Benchmark aus anderen Videos prüfen, ob der eingesetzte Rechner zB wegen treiberprobleme langsamer ist als erwartet.

Man kann aber auch alternativ hier immer wieder nach göttlicher Hilfe Fragen um die eigenen Probleme von dritten kostenlos gelöst zu bekommen

Ist halt immer eine Frage der Einschätzung, was die verschwendete eigene Arbeitszeit oder die fehlende Zufriedenheit der eigenen Kunden an externen Beratungsaufwand rechtfertigen. Muss ja jeder selber wissen, die erste Problemschilderung war ja auch erst am 1.Mai dieses Jahres .....

jobo 4. Sep 2020 06:23

AW: Insert into
 
Zitat:

Zitat von IBExpert (Beitrag 1472947)
Wenn es eine einigermaßen aktuelle Firebird Version ist und man einie einigermaßen aktuelle IBExpert Version haben sollte, dann sollte sich durch das Ansehen dieses Videos https://www.youtube.com/watch?v=MOuCeKTNpOk innerhalb kurzer Zeit mit der Traceapi finden lassen, was der grund ist ..

Man kann aber auch alternativ hier immer wieder nach göttlicher Hilfe Fragen um die eigenen Probleme von dritten kostenlos gelöst zu bekommen
.....

Ja, eine Analyse ist möglich und sicher nicht unsinnig.

Über den Punkt "kostenlos" war der TE allerdings schon hinaus, er hat explizit nach bezahlter Hilfe gefragt. Ich hab leider keine Zeit für sowas.

himitsu 4. Sep 2020 08:05

AW: Insert into
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1472854)
Wenn man große Datenmengen in 'ne Tabelle einfügt, kann es durchaus hilfreich sein, alle für das Insert nicht benötigten Indices zu löschen

Wieso muß man immer gleich löschen?

Viele DBMS bieten es an Indize umd vor allem Constraint zu deaktivieren
und anschließend mit oder ohne Prüfung wieder anzuschalten.

Ohne = falsche Constraint werden erlaubt und werden erst beim nächsen EDIT des jeweiligen Datensatzes wieder geprüft.

Es ist zwar speziell für Backup/Restore ganz praktisch, um nicht die Reihenfolge der Tabellen beachten zu müssen, aber auch für Massen-Bearbeitungen (Insert/Edit/Delete), wenn man selbst die Daten geprüft hat, oder die Prüfungen erst auch "Einmal" erledigen lassen will, wenn man alles drin hat (vor Abschluss der Session).

Delphi.Narium 4. Sep 2020 09:20

AW: Insert into
 
Also "löschen" ist etwas "hart" formuliert.

Sagen wir so: Es könnte eventuell hilfreich sein, die für das entsprechende Datenbanksystem sinnvollste und am wenigsten aufwendige Möglichkeit der vorübergehenden Nichtnutzung der für die konkrete Aufgabe entbehrlichen Indizes zu wählen.

Hängen an Indizes irgendwelche Abhängigkeiten, die für die korrekte Speicherung von Daten erforderlich sind, darf man die natürlich weder löschen noch deaktivieren, noch ...

Mit somaleben machen wir da mal was, Hauptsache ein bestimmter Job wird dadurch schneller, ist es nichts. Man muss sich da schon jeden Eingriff in die Datenbank sehr genau überlegen und sicherstellen, dass der Eingriff nicht irgendwelche "Spätfolgen" verursacht, die erst Sekunden, Minuten, Tage, Wochen, Monate, ... später unter Umständen eventuell vielleicht auftreten könnten oder sofort zu Inkonsistenzen ... führen können.

Die Mutter ist die Vorsicht der Porzellankiste (oder so ähnlich;-))

Oder Anders: Aus den hier vorliegenden Informationen kann man keine sinnvolle und garantiert hilfreiche und korrekte Lösung des Problems entwickeln, es ist allenfalls möglich ein paar Lösungsansätze, die eventuell zielführend sein könnten, zu geben. Die Lösung könnte auch aus 'ner Kombination der bisher gemachten Vorschläge aller Threadteilnehmer bestehen. Aber selbst das kann man nicht mit Sicherheit sagen.

hoika 4. Sep 2020 09:40

AW: Insert into
 
Hallo,
ich würde mir erst mal das Video von Holger reinziehen (bin etwa bei 50%, puh viel Know-How ...).
Und genau ein Insert per Trace-API verfolgen.

jobo 4. Sep 2020 10:36

AW: Insert into
 
Zitat:

Zitat von himitsu (Beitrag 1472970)
Zitat:

Zitat von Delphi.Narium (Beitrag 1472854)
Wenn man große Datenmengen in 'ne Tabelle einfügt, kann es durchaus hilfreich sein, alle für das Insert nicht benötigten Indices zu löschen

Wieso muß man immer gleich löschen?

Viele DBMS bieten es an Indize umd vor allem Constraint zu deaktivieren
und anschließend mit oder ohne Prüfung wieder anzuschalten.

Ohne = falsche Constraint werden erlaubt und werden erst beim nächsen EDIT des jeweiligen Datensatzes wieder geprüft.
..

Das ist glaub ich nicht ganz richtig. Ein Constraint existiert ganz allgemein erstmal ohne Index, nur seine Prüfung ist mit Index oft viel schneller. Das kommt darauf an, ob
1. überhaupt ein xy Key Constraint (primär oder fk) genutzt wird oder ein bloßer Value Constraint (im gleichen Datensatz)
2. das RDBMS die Handhabung von Key Constraints und Indizierung sauber trennt

Gerade 2. ist leider nicht immer gegeben und sehr unterschiedlich "komfortabel" realisiert, je nach Hersteller. Das Löschen eines Constraints löscht also den Index und vielleicht auch umgekehrt(?). Auch (deffered) Aktivierung / Deaktivierung ist meines Wissens nicht überall verfügbar.
Gute Systeme können bspw. (alte) bestehende Indizes nutzen, um neue (oder sich selbst) neu aufzubauen, den Aufbau parallelisieren, ...

Aber das geht schon etwas weit vom Thema ab.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:24 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz