Firebird SQL - Prozeduren: Datensatz kann von einem Programm nicht gelöscht werden

Themen rund um den praktischen Einsatz von Firebird. Fragen zu SQL, Performance, Datenbankstrukturen, etc.

Moderator: thorben.braun

Antworten
Iceboone
Beiträge: 4
Registriert: Di 14. Jul 2020, 15:27

Mi 15. Jul 2020, 09:38

Hallo,

ich habe eine Frage. Ich habe in einem Verwaltungsprogramm versucht einen Datensatz, den ich per "Insert To"- Befehl geschrieben habe mit dem Programm zu löschen. Dies funktioniert nicht mit folgender Fehlermeldung:

Der Datensatz wurde schon von einem anderen Benutzer verarbeitet.

Die Tabelle wird aktualisiert.

Wenn ich den Datensatz dann öffne und manuell speicher, kann ich den Datensatz danach löschen.

Meine Frage zielt auf Prozeduren ab. Ich habe jetzt in der SQL-Tabelle gesehen, dass es eine Prozedur gibt, die nach DELETE, INSERT oder UPDATE getriggert wird. Dieser Trigger scheint aber nur durch das Programm selbst ausgelöst zu werden. Kann man solche Trigger bzw. Prozeduren manuell per Befehl starten?

Für jeden Tipp wäre ich dankbar.

Vielen Dank im Voraus.

Gruß Volker

P.S.:

Hier wäre der Trigger, der ausgelöst wird:

Code: Alles auswählen

CREATE TRIGGER CL_COURTAGE FOR COURTAGE AFTER INSERT OR UPDATE OR DELETE
AS
DECLARE VARIABLE sUSER_NAME VARCHAR(15);
DECLARE VARIABLE AKTIV INTEGER;
DECLARE VARIABLE ISEVENT VARCHAR(9);
DECLARE VARIABLE REFAMSIDNR VARCHAR(15);
DECLARE VARIABLE XML VARCHAR(32000);
DECLARE VARIABLE sDESCRIPTION VARCHAR(150);
DECLARE VARIABLE sTABLEINFO VARCHAR(50);
declare variable "AGENTURNR" VARCHAR(1000);
declare variable "COURTFORMEL" VARCHAR(1000);
declare variable "COURTFORMELZV" VARCHAR(1000);
declare variable "INSERTDATE" VARCHAR(1000);
declare variable "INSERTTIME" VARCHAR(1000);
declare variable "INSERTUSER" VARCHAR(1000);
declare variable "MGEBUEHR" VARCHAR(1000);
declare variable "MONATE" VARCHAR(1000);
declare variable "PRODUKT" VARCHAR(1000);
declare variable "PROZENT" VARCHAR(1000);
declare variable "PROZENTZV" VARCHAR(1000);
declare variable "SPARTE" VARCHAR(1000);
declare variable "STICHTAG" VARCHAR(1000);
declare variable "TEXT" VARCHAR(1000);
declare variable "ZAHLWEISE" VARCHAR(1000);
BEGIN
SELECT COUNT(*) FROM changelog_config WHERE UPPER(TABLENAME)='COURTAGE' INTO :aktiv;
if (aktiv > 0) then
BEGIN
if (new.bearbdat = old.bearbdat AND new.bearbzeit = old.bearbzeit) then
  sUSER_NAME = 'SYSTEM_BATCH';
else if (new.bearbeiter is null) then
  sUSER_NAME = 'SYSTEM_UNK';
else 
  sUSER_NAME = new.bearbeiter;
if (deleting) then
begin
  sUSER_NAME = 'unbekannt';
  isevent = (select cast((_ISO8859_1 X'4CF6736368656E') as varchar(9) character set ISO8859_1) from rdb$database);
  REFAMSIDNR = old.AMSIDNR;
end
else
begin
  REFAMSIDNR = new.AMSIDNR;
  if (inserting) then
    isevent = 'Neuanlage';
  else
    isevent = (select cast((_ISO8859_1 X'C46E646572756E67') as varchar(9) character set ISO8859_1) from rdb$database);
end
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."AGENTURNR", new."AGENTURNR", 'C', 0, 'AGENTURNR' returning_values "AGENTURNR";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."COURTFORMEL", new."COURTFORMEL", 'C', 0, 'COURTFORMEL' returning_values "COURTFORMEL";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."COURTFORMELZV", new."COURTFORMELZV", 'C', 0, 'COURTFORMELZV' returning_values "COURTFORMELZV";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."INSERTDATE", new."INSERTDATE", 'D', 0, 'INSERTDATE' returning_values "INSERTDATE";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."INSERTTIME", new."INSERTTIME", 'T', 0, 'INSERTTIME' returning_values "INSERTTIME";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."INSERTUSER", new."INSERTUSER", 'C', 0, 'INSERTUSER' returning_values "INSERTUSER";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."MGEBUEHR", new."MGEBUEHR", 'N', 2, 'MGEBUEHR' returning_values "MGEBUEHR";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."MONATE", new."MONATE", 'I', 0, 'MONATE' returning_values "MONATE";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."PRODUKT", new."PRODUKT", 'C', 0, 'PRODUKT' returning_values "PRODUKT";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."PROZENT", new."PROZENT", 'N', 2, 'PROZENT' returning_values "PROZENT";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."PROZENTZV", new."PROZENTZV", 'N', 2, 'PROZENTZV' returning_values "PROZENTZV";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."SPARTE", new."SPARTE", 'C', 0, 'SPARTE' returning_values "SPARTE";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."STICHTAG", new."STICHTAG", 'D', 0, 'STICHTAG' returning_values "STICHTAG";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."TEXT", new."TEXT", 'C', 0, 'TEXT' returning_values "TEXT";
EXECUTE PROCEDURE CHANGELOG_DEFAULT old."ZAHLWEISE", new."ZAHLWEISE", 'C', 0, 'ZAHLWEISE' returning_values "ZAHLWEISE";

 XML = '<CHANGES>';
 XML = XML||"AGENTURNR"||"COURTFORMEL"||"COURTFORMELZV"||"INSERTDATE"||"INSERTTIME"||"INSERTUSER"||"MGEBUEHR"||"MONATE"||"PRODUKT"||"PROZENT"||"PROZENTZV"||"SPARTE"||"STICHTAG"||"TEXT"||"ZAHLWEISE"||'</CHANGES>';
  if (deleting) then
    sDESCRIPTION=substring(coalesce(old."SPARTE", '')||'/'||coalesce(old."TEXT", '')||'/'||coalesce(old."STICHTAG", '') FROM 1 for 150);
  else
    sDESCRIPTION=substring(coalesce(new."SPARTE", '')||'/'||coalesce(new."TEXT", '')||'/'||coalesce(new."STICHTAG", '') FROM 1 for 150);
 sTABLEINFO=(select cast((_ISO8859_1 X'436F7572746167656E') as varchar(150) character set ISO8859_1) from rdb$database);
 if (XML != '<CHANGES></CHANGES>') then
INSERT INTO CHANGELOG (CHANGELOGID, BEARBDAT, BEARBZEIT, BEARBEITER, DESCRIPTION, TABLENAME, TABLENAME_DISP, "KEY", STATUSEVENT, CHANGES) VALUES (gen_id(CHANGELOG_ID, 1), current_date, current_time, :sUSER_NAME, :sDESCRIPTION ,'COURTAGE', :sTABLEINFO ,:refAMSIDNR, :isEvent ,:XML);
 WHEN SQLCODE -802 DO
 begin
  sDESCRIPTION=(select cast((_ISO8859_1 X'DC6265726C6175666665686C6572') as varchar(150) character set ISO8859_1) from rdb$database);
INSERT INTO CHANGELOG (CHANGELOGID, BEARBDAT, BEARBZEIT, BEARBEITER, DESCRIPTION, TABLENAME, TABLENAME_DISP, "KEY", STATUSEVENT, CHANGES) VALUES (gen_id(CHANGELOG_ID, 1), current_date, current_time, :sUSER_NAME, :sDESCRIPTION, 'COURTAGE', :sTABLEINFO, :refAMSIDNR, 'Fehler', '<ERROR>CONCAT_OVERFLOW</ERROR>');
 end
end
end
bfuerchau
Beiträge: 189
Registriert: Mo 7. Mai 2018, 18:09

Mi 15. Jul 2020, 14:07

Trigger können nicht manuell ausgelöst werden. DIe Definition ist dafür zuständig: before/after insert/update/delete.

"Der Datensatz wurde schon von einem anderen Benutzer verarbeitet." ist keine Meldung der Firebird sondern deines verwendeten Frameworks.
Wenn du (z.B. in C#) ein DataTable-Objekt und einen DataAdapter verwendest, werden SQL's i.d.R. vom Framework generiert.
Hier werden bei Update/Delete gerne zusätzliche Where-Klauseln erzeugt:

update mytable set f1=newf1 where f1=oldvalue ...

Wenn nun ein Trigger die Daten verändert, weiß dein Framework nichts davon. Um also sicher zu gehen, dass Daten gelöscht werden könnnen, muss u.U. ein Refresh der DataTable erfolgen.

Aber selbst ein Refresh kann falsch liegen, wenn man keine neue Transaktion gestartet hat, da man dann nur Altdaten bekommt (Satzversion) so dass der Update/Delete wieder fehlschlägt, da hierbei die Current-Version verwendet wird und diese sich wieder geändert haben kann.

Etwas einfacher wird es dann, wenn man statt "Select * ..." gezielt die Felder, die man benötigt, lädt, also "Select F1, F2 from MyTable". Werden nun andere Felder geändert, weiß dein Framework ja nichts davon und schon klappt der Update/Delete.
Iceboone
Beiträge: 4
Registriert: Di 14. Jul 2020, 15:27

Mi 15. Jul 2020, 19:00

Also ich schreibe ein Skript in Javascript. Mit diesem Skript lade ich Daten aus einer csvDatei, um sie dann per Insert-Befehl einzufügen.

Hierfür habe ich mir eine Inserthelper-Funktion gebaut. Im gesamten Skript steht kein Insert und kein Update-Statement.

Das Skript liest also im Javascript diese Datei ein und sendet dann in diesem Fall 705 Insert--Befehle per Runtime.ExecuteQuery in die Datenbank.

Die Tabelle beinhaltet 20 Spalten von denen ich 14 schreibe. Wenn ich im System den Datensatz ändere ist das einzige was passiert, dass er eine der nicht beschriebenen Spalten (BEARBZEIT) mit der aktuellen Uhrzeit belegt. Kann es sein, dass wenn das die Spalte mit der Zeit nicht befüllt ist, dass er denkt es wäre noch in Bearbeitung? Wenn ja, wie ist der SQL-Befehl für die aktuelle Zeit? CURRENT_TIME?
Benutzeravatar
martin.koeditz
Beiträge: 220
Registriert: Sa 31. Mär 2018, 14:35

Do 16. Jul 2020, 13:30

CURRENT_TIME ist die aktuelle Transaktionszeit. Diese ändert sich während der gesamten Transaktion nicht.

Mit NOW kann jederzeit die aktuelle Systemzeit ermittelt werden:

Code: Alles auswählen

select cast('NOW' as timestamp) from rdb$database
-- returns e.g. 2008-08-13 14:20:19.6170
Gruß
Martin
Martin Köditz
it & synergy GmbH
Iceboone
Beiträge: 4
Registriert: Di 14. Jul 2020, 15:27

Mi 26. Aug 2020, 10:36

So. Ich habe ganz vergessen Bescheid zu sagen, dass ich die Lösung gefunden habe.

Es war das Bearbeitungszeit. Das System dachte tatsächlich, das er noch mit anlegen beschäftigt ist weil die Zeit nicht befüllt war.

Danke für die HIlfe.
Antworten