Autor Beitrag
Carla
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 111
Erhaltene Danke: 2



BeitragVerfasst: Di 01.07.08 14:10 
Hallo,
ich habe ein Problem, welches wohl mit der Transactionssteuerung zusammenhängt.

Ich möchte eine Reihe Datensätze updaten oder wenn sie noch nicht vorhanden sind, inserten.
Dazu habe ich mir eine stored Procedure geschrieben, welche prüft, ob der Datensatz schon vorhanden ist.
Ist das der Fall wird die SatzID zurückgegeben. Ist der Datensatz noch nicht da, dann wird dieser angelegt.
Die SatzID wird von einem Generator geholt und zurückgegeben.

Der Ablauf:
StartTransaction;
ID := GetStoredProcedure(Nr,Abtlg).

Update Table (ID,.......

.
.
.

Commit;

Ist der Satz da, funktioniert alles wie gewünscht.
Ist der Satz noch nicht da, wird er von der Stored Procedure abgelegt und initialisiert. Die vom Generator bereitgestellte ID wird als Funktionswert zurückgegeben.
Das nachfolgende Update schlägt aber fehl, die der Satz noch nicht bekannt ist.

Wes funktioniert ist
Starttransaction;
StoredProcedure
Commit
StartTransaction
Update
Commit.

Hat wer eine Idee, was man ändern könnte?
GRuß Carla
Lemmy
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: Mi 02.07.08 07:19 
Hi Carla,

2 Dinge:

1. das Programm debuggen und nach dem 1. Commit (nach dem Aufruf der SP) mit einem Konsolenprogramm in der DB schauen ob der Datensatz da ist oder nicht
2. das Commit; StartTransaction; zwischen der Sp und dem Update weglassen.

ach noch was:

Haben die beiden Komponenten mit denen du das machst (Udpate und SP) die selbe Database und Transaction zugewiesen?

Grüße
Lemmy
hansa
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: Mi 02.07.08 07:36 
Poste mal bitte die SP aus der DB und den relevanten Teil des Programms.

_________________
Gruß
Hansa
Carla Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 111
Erhaltene Danke: 2



BeitragVerfasst: Mi 02.07.08 07:59 
user profile iconLemmy hat folgendes geschrieben:
Hi Carla,

2 Dinge:

1. das Programm debuggen und nach dem 1. Commit (nach dem Aufruf der SP) mit einem Konsolenprogramm in der DB schauen ob der Datensatz da ist oder nicht
2. das Commit; StartTransaction; zwischen der Sp und dem Update weglassen.

ach noch was:

Haben die beiden Komponenten mit denen du das machst (Udpate und SP) die selbe Database und Transaction zugewiesen?

Grüße
Lemmy


Ja, ich verwende IBDAC und beide Componenten haben die gleiche Transaction und Database ohnehin. Ich verwende FB 2.0.
Der Insert in der Datenbank in der SP ist erst nach dem Schließen und erneuten Öffnen der Transaction sichtbar.
Die ID des neu eingefügten Datensatzes wird korrekt von der SP zurückgegeben.
Demnächst steht FB2.1 an, da werde ich wohl auf "Insert or Update" mit Returnparameter ausweichen können.

Ich habe jetzt erst mal so gelöst:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Starttransaction;
  
While newDataRecord do
begin
  ID := GetStoredProcedure(Nr,Abtlg).
  CommitRetaining;
  Update Table (ID,.
end;

Commit;
hansa
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: Mi 02.07.08 08:51 
Wie gesagt, zeige mal die SP. Die Entscheidung über update oder insert überlässt man besser der DB.

_________________
Gruß
Hansa
Carla Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 111
Erhaltene Danke: 2



BeitragVerfasst: Mi 02.07.08 09:56 
user profile iconhansa hat folgendes geschrieben:
Die Entscheidung über update oder insert überlässt man besser der DB.


Das ist mit Verlaub gesagt Unsinn.

Ich stelle fest ob die gewünschte Artikelnummer bereits in der Datenbank ist oder nicht.
Ist sie nicht in der Datenbank, dann lege ich einen neuen - vorinitialisierten Datensatz an.Das Problem ist, dass die stored Procedure wohl in einer anderen Transaction läuft als das Anwenderprogramm, was diese SP aufruft.
Die SP selbt ist eher trivial.

ausblenden SQL-Anweisung
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
SET TERM ^ ;

CREATE OR ALTER PROCEDURE ADD_ARTIKEL (
    nr integer,
    abtlg integer)
returns (
    arid integer)
as
declare variable id integer;
declare variable curtime timestamp;
begin
  select count(*) from ARTIKEL WHERE ARTNR=:Nr and Abtlg=:Abtlg INTO :ID;
  if (ID > 0) then
  begin
    Select AID FROM ARTIKEL WHERE ARTNR=:Nr and Abtlg=:Abtlg INTO :ARID;
    suspend;
    exit;
  end

  ID = GEN_ID(GEN_LINKID, 1);
  arid = ID;
  curtime = current_timestamp;

  Insert Into ARTIKEL(...

Das Problem ist vorerst aber mit CommitRetaining gelöst.

Carla

Moderiert von user profile iconmatze: SQL-Tags hinzugefügt
hansa
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: Mi 02.07.08 10:11 
user profile iconCarla hat folgendes geschrieben:
Das ist mit Verlaub gesagt Unsinn.


Das, was du da machst ? Ja, das stimmt. :lol:

Die DB macht das so :

ausblenden SQL-Anweisung
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
CREATE PROCEDURE INSUPD_SP8 (ID_ART INTEGER)
AS
DECLARE VARIABLE AENDERN INTEGER;
BEGIN
  AENDERN = -1;
  SELECT ID FROM TABLEX WHERE ID_ART= :ID_ART INTO :AENDERN;
  IF (AENDERN < 0) THEN BEGIN
    INSERT INTO ...
  END
  ELSE BEGIN
    UPDATE ... SET ... WHERE (ID_ART = :ID_ART);
  END
  SUSPEND;
END^


Wird die vom Generator gelieferte ID sofort gebraucht, dann verwende eben "Returning".

_________________
Gruß
Hansa
Lemmy
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: Mi 02.07.08 10:38 
user profile iconCarla hat folgendes geschrieben:
user profile iconhansa hat folgendes geschrieben:
Die Entscheidung über update oder insert überlässt man besser der DB.


Das ist mit Verlaub gesagt Unsinn.


Nein nicht unbedingt. So was habe ich bei mir auch schon ein paar mal verwendet.

was mich aber noch interessieren würde: Was machst Du mit dem Timestamp? Wenn der wichtig ist, dann würde es sich gerade hier anbieten den Timestamp vom Server ebenfalls gleich erzeugen zu lassen. Das Problem, dass die Clientrechner eine falsche Uhrzeit haben ist dann schon mal ausgeschlossen....

Grüße
Lemmy
Carla Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 111
Erhaltene Danke: 2



BeitragVerfasst: Mi 02.07.08 10:54 
user profile iconLemmy hat folgendes geschrieben:

was mich aber noch interessieren würde: Was machst Du mit dem Timestamp? Wenn der wichtig ist, dann würde es sich gerade hier anbieten den Timestamp vom Server ebenfalls gleich erzeugen zu lassen. Das Problem, dass die Clientrechner eine falsche Uhrzeit haben ist dann schon mal ausgeschlossen....


Current_time wird in der stored procedure geholt und die läuft ja in der Datenbank auf dem Server.
Es handelt sich bereits um die Serverzeit.

Ein Insert oder Update in der Sp ist wenig sinnvoll.
Die Tabelle enthält etwa 200 Felder, von welchen jeweils 30 bis 40 in unterschiedlichen Kombinationen benötigt werden.
Die SP für das Insert soll mir nur einen neuen Datensatz korrekt initialisieren. (z.B. alle Flag auf 0 statt null setzen.
Bestimmte Bearbeitungskennungen auf Standardwert u.s.w.
Der Datensatz selbst kommt in einer XML Datei als Rohwert an.
Im Programm wird dann die Updateanweisung aufbereitet, die von Datensatz zu Datensatz unterschiedlich sein kann.
z.B. Gewicht oder Stückzahl oder unterschiedliche Verrechnungseinheiten.
Mein Problem ist, dass eine SP in der Datenbank wohl nicht in der gleichen Transaction wie der Client, der diese
benötigt, arbeitet.
Ich will jetzt probieren, die SP abzuschaffen und die Initialisierungen in einen Trigger "Before Insert" unterzubringen.
Dann muss ich allerdings vom Programm aus prüfen, ob der Artikel bereits vorhanden ist.

Gruß Carla
Lemmy
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: Mi 02.07.08 11:33 
user profile iconCarla hat folgendes geschrieben:

Current_time wird in der stored procedure geholt und die läuft ja in der Datenbank auf dem Server.
Es handelt sich bereits um die Serverzeit.


War in deinem SP-Auszug nicht dabei - dann ist es OK

user profile iconCarla hat folgendes geschrieben:

Ein Insert oder Update in der Sp ist wenig sinnvoll.
Die Tabelle enthält etwa 200 Felder, von welchen jeweils 30 bis 40 in unterschiedlichen Kombinationen benötigt werden.


Ich habe auch nicht behauptet dass es immer sinnvoll ist. In deinem Fall dann sicherlich nicht. Aber bei 200 Felder könnte man sich schon wieder um die DB-Modellierung evtl. streiten ;-)



so... eben mal getestet. Server: Firebird 2.0.3 mit FlameRobin. Einfach DB (eine Tabelle mit 2 Feldern und eine InsertSP):

ausblenden SQL-Anweisung
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
CREATE TABLE SPTEST(
  ID Integer,
  NAME Varchar(50)
);

CREATE PROCEDURE SPINSUP (    SPID Integer,
    SPNAME Varchar(50) )
AS
Begin 
  INSERT INTO SPTest Values (:SPID, :SPName); 
end^
SET TERM ; ^


Die SP wird im Kontext der laufenden Transaktion ausgeführt. Kenne ich ehrlich gesagt seit FB 1 (bzw. IB 6.0) auch nicht anders. Lediglich DDL Anweisungen (Create usw.) laufen außerhalb des Transaktionsmechanismus.

Was ich mir jetzt noch denken könnte (habe keine Erfahrung mit IBDAC): Dass intern bei der StoredProcedure Komponente von IBDAC noch ein automatisches Transaction-Handling stattfindet - oder ein generelles automatisches Transaction Handling abläuft. Es ist auf jeden Fall sehr seltsam...

GRüße
Lemmy