DELETEs loggen - Empfehlungen

Datenbanklösungen mit AOO/LO

Moderator: Moderatoren

oliver1804
**
Beiträge: 39
Registriert: Fr, 27.11.2009 15:07

DELETEs loggen - Empfehlungen

Beitrag von oliver1804 »

Hallo liebe Gemeinde ;-);

ich benutze BASE (OOo 3.3.0 RC7) per ODBC (http://www.ch-werner.de/sqliteodbc/) als Frontend für eine sqlite-DB (http://www.sqlite.org/) und möchte erfassen, wer zu welchem Zeitpunkt in welcher Tabelle welche Datensätze gelöscht hat. Dazu hab ich mir folgende Log-Tabelle erstellt:

Code: Alles auswählen

CREATE TABLE ylog_DELETED_BY(
			id                                    INTEGER PRIMARY KEY AUTOINCREMENT,
			id_staff_editor                       INTEGER NOT NULL,
			table_with_deletion                   TEXT    NOT NULL,
			pk_deleted                            INTEGER NOT NULL, -- gelöschter Primärschlüssel
			created                               INTEGER NOT NULL DEFAULT (strftime('%s','now')) -- automatischer Zeitstempel bei INSERT (Satzanlage)
		);
Nun benutze ich in Tabellen auch Foreign Key Constraints, so daß ein DELETE eben auch mal mit "is nich" quittiert wird.

Ich sehe prinzipiell 2 Möglichkeiten, das nötige Makro auszulösen:
1. per Formular-Event
2. per Schaltflächen-Event

Momentan seh ich nicht, daß diese Funktionalität per Formular-Event möglich wäre, so daß ich nun gezwungen bin, fürs Löschen Schaltflächen zu erstellen (und andere Wege zum Löschen blockieren muß). Da es sich um eine grundsätzliche Entscheidung handelt, die alle Formulare betrifft, würd ich mich über Kritik und Anregungen sehr freuen.

Danke
Oliver
Barlee
******
Beiträge: 767
Registriert: Sa, 17.12.2005 12:27

Re: DELETEs loggen - Empfehlungen

Beitrag von Barlee »

Hallo Oliver,

hast Du Dir schon Trigger angeschaut? Käme vielleicht für Dich in Frage, um ohne Makros auszukommen:
http://www.sqlite.org/lang_createtrigger.html

Gruß Barlee
oliver1804
**
Beiträge: 39
Registriert: Fr, 27.11.2009 15:07

Re: DELETEs loggen - Empfehlungen

Beitrag von oliver1804 »

Moin Barlee,
Barlee hat geschrieben:Hallo Oliver,

hast Du Dir schon Trigger angeschaut? Käme vielleicht für Dich in Frage, um ohne Makros auszukommen:
http://www.sqlite.org/lang_createtrigger.html

Gruß Barlee
ich arbeite bereits ausgiebig damit; hier mal ein Beispiel einer einfachen Tabelle nebst Triggern und LogTabelle:

Code: Alles auswählen

CREATE TABLE staff(
			id                                INTEGER     PRIMARY KEY AUTOINCREMENT,
			id_staff_editor                   INTEGER     NOT NULL,
			code                              CHAR(2)     NOT NULL, -- zweistelliges Kürzel in Grossbuchstaben
			forename                          VARCHAR     NOT NULL,
			name                              VARCHAR     NOT NULL,
			UNIQUE(code),
			CONSTRAINT                        CODE_enforce_2_uppercase_letters CHECK(code GLOB '[A-Z][A-Z]')
		);

CREATE TRIGGER log_DELETE_staff
			AFTER DELETE ON staff FOR EACH ROW
			BEGIN
				INSERT INTO xlog_staff(idOLD,id_staff_editorOLD,codeOLD,forenameOLD,nameOLD,sqlaction) VALUES(old.id,old.id_staff_editor,old.code,old.forename,old.name,'D');
			END;

CREATE TRIGGER log_INSERT_staff
			AFTER INSERT ON staff FOR EACH ROW
			BEGIN
				INSERT INTO xlog_staff(idNEW,id_staff_editorNEW,codeNEW,forenameNEW,nameNEW,sqlaction) VALUES(new.id,new.id_staff_editor,new.code,new.forename,new.name,'I');
			END;

CREATE TRIGGER log_UPDATE_staff
			AFTER UPDATE ON staff FOR EACH ROW
			BEGIN
				INSERT INTO xlog_staff(idNEW,idOLD,id_staff_editorNEW,id_staff_editorOLD,codeNEW,codeOLD,forenameNEW,forenameOLD,nameNEW,nameOLD,sqlaction) VALUES(new.id,old.id,new.id_staff_editor,old.id_staff_editor,new.code,old.code,new.forename,old.forename,new.name,old.name,'U');
			END;

CREATE TABLE xlog_staff(
			LOGID                             INTEGER     PRIMARY KEY AUTOINCREMENT,
			idNEW                             INTEGER,
			idOLD                             INTEGER,
			id_staff_editorNEW                INTEGER,
			id_staff_editorOLD                INTEGER,
			codeNEW                           TEXT,
			codeOLD                           TEXT,
			forenameNEW                       TEXT,
			forenameOLD                       TEXT,
			nameNEW                           TEXT,
			nameOLD                           TEXT,
			sqlaction                         TEXT NOT NULL,
			created                           INTEGER NOT NULL DEFAULT (strftime('%s','now')) -- automatischer Zeitstempel bei INSERT (Satzanlage)
		);
Das Prinzip ist die Speicherung von Zuständen (bei einem INSERT die Werte des neuen Datensatzes, bei einem UPDATE die alten u n d neuen Werte und bei einem DELETE die alten - in der Tabelle nicht mehr existenten - Werte) mit den Zusatzangaben "Zeitpunkt der Änderung (created)", "Art der Änderung (sqlaction)" sowie "Ändernder (id_staff_editorNEW)" nach Durchführung der Aktion (AFTER DELETE/INSERT/UPDATE im TRIGGER-Statement).

Bei einem DELETE ist der neue Zustand das "Nichtvorhandensein des Datensatzes" und der alte Zustand der gelöschte Datensatz in der unberührten Form; hieraus ergibt sich dann, daß der für die Löschung Verantwortliche nicht erfaßt wird. Bisher hab ich mir damit geholfen, daß ich vor jedem DELETE ein UPDATE von "id_staff_editor" durchgeführt habe, was aber aus 2 Gründen nicht sauber ist:

1. konzeptionell, da die Änderung programmatisch durchgeführt wird
2. in der Durchführung, da ein UPDATE von "id_staff_editor" auch dann durchgeführt wird, wenn ein DELETE aufgrund der Verletzung eines Constraints von der DB abgelehnt wird.

any ideas?

Oliver
DPunch
*******
Beiträge: 1112
Registriert: Mo, 02.11.2009 16:16
Wohnort: Marburg

Re: DELETEs loggen - Empfehlungen

Beitrag von DPunch »

Aloha
oliver1804 hat geschrieben:1. konzeptionell, da die Änderung programmatisch durchgeführt wird
Auf welche Weise bzw. wo liest Du denn die ID der Person, die die Änderung durchführt, aus?
oliver1804 hat geschrieben:2. in der Durchführung, da ein UPDATE von "id_staff_editor" auch dann durchgeführt wird, wenn ein DELETE aufgrund der Verletzung eines Constraints von der DB abgelehnt wird.
Dafür bietet Sqlite die RAISE-Funktion, mit der Du Deine UPDATE- bzw DELETE-Anweisung abbrechen kannst, die entsprechende Sicherheitsabfrage musst Du natürlich formulieren.

Code: Alles auswählen

(...)
RAISE(ABORT, 'Es ist ein Constraint-Konflikt aufgetreten')
(...)
oliver1804
**
Beiträge: 39
Registriert: Fr, 27.11.2009 15:07

Re: DELETEs loggen - Empfehlungen

Beitrag von oliver1804 »

Mahlzeit,
DPunch hat geschrieben:Aloha
oliver1804 hat geschrieben:1. konzeptionell, da die Änderung programmatisch durchgeführt wird
Auf welche Weise bzw. wo liest Du denn die ID der Person, die die Änderung durchführt, aus?
Beim Öffnen der .odb wird eine Listbox mit "Vorname Name (Kürzel)" aus der Mitarbeitertabelle gefüllt. Durch Markieren und Bestätigen wird eine ID (PK der Mitarbeitertabelle) in eine globale Variable geschrieben. Die Anmeldung erfolgt also ohne Kennwort und man kann sich als beliebige Person anmelden.
DPunch hat geschrieben:
oliver1804 hat geschrieben:2. in der Durchführung, da ein UPDATE von "id_staff_editor" auch dann durchgeführt wird, wenn ein DELETE aufgrund der Verletzung eines Constraints von der DB abgelehnt wird.
Dafür bietet Sqlite die RAISE-Funktion, mit der Du Deine UPDATE- bzw DELETE-Anweisung abbrechen kannst, die entsprechende Sicherheitsabfrage musst Du natürlich formulieren.

Code: Alles auswählen

(...)
RAISE(ABORT, 'Es ist ein Constraint-Konflikt aufgetreten')
(...)
Bei der jetzigen Vorgehensweise muß immer zunächst ein UPDATE des existierenden Datensatzes erfolgen (ID des zukünftigen Löschers) und dann das DELETE. Erst das DELETE ergibt dann, ob dies zulässig ist oder aufgrund eines Constraints eben nicht. Das Ganze müßte also im Rahmen einer Transaktion mit eventuellem Rollback durchgeführt werden, was mir aber im Moment gedanklich nicht so paßt, da ich sicherlich die jetzige TRIGGER-Lösung überdenken müßte, da der ROLLBACK sich auch auf das UPDATE erstrecken müßte. Weiterhin scheint mir das konzeptionell nicht in Ordnung zu sein, da ja der letzte ZUstand eines Datensatzes gespeichert werden soll (betrifft das ganze TRIGGER-Konzept) und es keinen neuen Zustand gibt (Datensatz ist "verschwunden"), so daß mir die Verwendung einer Extra-Tabelle fürs Loggen der ID des Löschenden sinnvoller erscheint (sh. auch meinen 1. Beitrag).

Siehst Du irgendwelche Inplausibilitäten oder Aspekte, die ich nicht bedacht habe? Man verrennt sich ja so leicht in etwas :-)

Gruß
Oliver
DPunch
*******
Beiträge: 1112
Registriert: Mo, 02.11.2009 16:16
Wohnort: Marburg

Re: DELETEs loggen - Empfehlungen

Beitrag von DPunch »

Aloha
oliver1804 hat geschrieben:Beim Öffnen der .odb wird eine Listbox mit "Vorname Name (Kürzel)" aus der Mitarbeitertabelle gefüllt. Durch Markieren und Bestätigen wird eine ID (PK der Mitarbeitertabelle) in eine globale Variable geschrieben.
Ok, da Du eh mit Makros arbeitest (arbeiten musst), muss man sich prinzipiell auch nicht weiter auf die Trigger versteifen.

Zwei schnelle Ideen dazu:
a) Statt die Originaldaten zu ändern (was aus von Dir schon genannten Gründen alles andere als sauber ist), überprüfe statt des direkten Updates mit der ID, ob FK-References auf den zu löschenden Datensatz in Child-Tables vorliegen (wie ich gelesen habe, war dies früher auch die einzige Möglichkeit, unter Sqlite die referenzielle Integrität sicher zu stellen), wenn ja, dann per RAISE das Update gar nicht erst durchführen, wenn nein, dann die ID schon von vornherein in einen neuen Datensatz der Log-Table schreiben, den Rest dann per AFTER DELETE-Trigger nachtragen.

b) Eine Tabelle anlegen, in die beim Öffnen der .odb einfach die ID des momentan aktiven Benutzers geschrieben wird (da Du ja eh beim Öffnen per Makro die ID ausliest und hinterlegst, kannst Du das auch einfach hintendran hängen). Beim AFTER DELETE-Trigger holst Du Dir dann aus dieser Tabelle die ID. Das ist dann eben das Workaround für die "richtige" Benutzerverwaltung, die man normalerweise beim Arbeiten mit DBMS zur Verfügung hat.

Ansonsten bliebe halt noch das komplette Arbeiten mit Makros.
oliver1804
**
Beiträge: 39
Registriert: Fr, 27.11.2009 15:07

Re: DELETEs loggen - Empfehlungen

Beitrag von oliver1804 »

Hallo DPunch,

ich dachte auch bereits an b) - ich werd das wohl über eine temporäre Tabelle machen, denn da wird die eigentliche db nicht angefaßt; muß halt noch dafür sorgen, daß die Verbindung bis zum Schließen der Datei bestehenbleibt (~Dokumenten Event) und bei Neuanmeldung ohne Schließen der bestehende Satz vorher gelöscht wird. Ich denke, daß das konzeptionell sauber ist und Komplexität aus dem Frontend nimmt.

Danke für die Diskussion
Oliver

EDIT:
Wenn ich so recht überlege, geht das auch nur mittels einer temporären Tabelle, die für jeden Nutzer den gleichen Namen hat (und eben nur lokal erreichbar ist) - denn der Trigger ist schließlich auch statisch (was ja wohl auch Sinn macht).

EDIT02:
tja, das mit der temporären Tabelle oder einer angehängten (ATTACHED) DB hat sich dann wohl erledigt; Auszug aus der Doku zu TRIGGERn:
The name of the table to be modified in an UPDATE, DELETE, or INSERT statement must be an unqualified table name. In other words, one must use just "tablename" not "database.tablename" when specifying the table. The table to be modified must exist in the same database as the table or view to which the trigger is attached.
EDIT03:
wer Interesse an Details hat: http://www.mail-archive.com/sqlite-user ... 57021.html
Antworten