Italian community of Lazarus and Free Pascal

Programmazione => Databases => Topic aperto da: cappe - Gennaio 03, 2023, 10:30:19 am

Titolo: Log
Inserito da: cappe - Gennaio 03, 2023, 10:30:19 am
Dovrei fare un log delle modifiche fatte ai database dagli utenti, secondo voi è meglio scriverlo a livello di trigger o di codice pascal. Qualche idea su come o qualche spunto?
Sono orientato di scriverlo in Pascal, ma ci sto pensando ...
Titolo: Re:Log
Inserito da: nomorelogic - Gennaio 03, 2023, 11:26:00 am
ciao
per farlo a livello di trigger dovresti avere gli utenti che accedono al DB ognuno col loro codice (intendo la connessione al DB)

in uno scenario dove l'applicazione si connette al DB sempre con lo stesso utente e poi l'utente è gestito dall'applicazione è meglio  farlo da codice pascal
Titolo: Re:Log
Inserito da: Stilgar - Gennaio 03, 2023, 02:06:03 pm
Ciao Cappe.

La risposta, come spesso capita in informatica, non è univoca.
Dipende dallo scenario che ti interessa (tanto per cambiare).

Se a te non serve salvare l'utente che opera la modifica, potrebbe essere il trigger che opera la storicizzazione del record modificato, una storicizzazione acefale, per capirsi.
Se devi mettere in piedi una sorta di audit, forse l'utente che opera la modifica ti serve e quindi le strategie devono cambiare (come suggerito da nomore potrebbe non essere lo stesso utente di connessione al db)
Se il DB che utilizzi permette di mettere in sessione dei valori custom, il trigger potrebbe essere ancora una strada praticabile: carici il nome dell'utente che modifica in sessione e il trigger lo legge.

Se a te serve una soluzione che non sia legata mani e piedi ad un engine specifico, la strada dell'applicazione esterna che gestisce l'utenza (come parametro nell'insert, per banalizzare l'operazione) sicuramente è più "portabile" tra i vari engine. Essendo 2 operazioni di modifica (storicizzazione + update) ricordati che devi gestire la transazione.
Storicizzare un record che poi si schianta nella modifica potrebbe portare a delle difficoltà e casini nel caso in cui l'ufficio audit volesse un report delle modifiche.

Spero di averti dato qualche spunto di riflessione.

Stilgar
Titolo: Re:Log
Inserito da: DragoRosso - Gennaio 03, 2023, 04:20:44 pm
Eseguire un Audit delle transazioni di un DB non è cosa semplice. Alcuni DB, anzi dire molti DB, dovrebbero già avere internamente la funzionalità richiesta (tra cui FIREBIRD e MSSQL se non vado errato).

SQLite invece presenta i normali Trigger che possono essere usati per fare creare un log. Però ti leghi specificatamente allo stato del DB e se fai variazioni delle tabelle ad esempio tramite update del tuo applicativo (cosa che ad esempio io faccio) bisogna ricordarsi di aggiornare anche i trigger con delle DDL.

Eseguendo tu a livello di codice i post, commit, etc ... io lo farei con gli eventi propri del componente (ZEOS ad esempio) tipo AfterPOST, AfterCommit, ......

Più flessibile ....

Ciao
Titolo: Re:Log
Inserito da: nomorelogic - Gennaio 03, 2023, 04:36:12 pm
cmq su vuoi passare per i trigger, ti dico come ho fatto io in qualche occasione

Titolo: Re:Log
Inserito da: cappe - Gennaio 03, 2023, 04:49:01 pm
Il DB è firebird. Grazie per il suggerimento.

Penso di farlo in pascal, Dovrei fare un log delle  modifiche degli utenti. Ma ci sto ancora pensando.
A me serve una cosa semplice.
Tabelle transazione e modifiche. Ci provo.

P.S. Non avevo visto l'altro post sui log anche se sembra essere diverso il contesto,

Titolo: Re:Log
Inserito da: Stilgar - Gennaio 03, 2023, 05:33:58 pm
Allora si può ragione su FB.


https://firebirdsql.org/file/documentation/html/en/refdocs/fblangref30/firebird-30-language-reference.html#fblangref30-ddl-trigger (https://firebirdsql.org/file/documentation/html/en/refdocs/fblangref30/firebird-30-language-reference.html#fblangref30-ddl-trigger)


Utilizzando 2 funzioni FB puoi "personalizzare" la sessione di lavoro dell'utente.
Codice: [Seleziona]

SELECT RDB$SET_CONTEXT ('USER_SESSION', 'OS_USER','UTENTE OS') FROM RDB$DATABASE;
Codice: [Seleziona]
SELECT RDB$GET_CONTEXT ('USER_SESSION', 'OS_USER') FROM RDB$DATABASE;


In questo modo puoi customizzare le sessioni e lavorare con i trigger, generalizzare le tue query (da pascal), adattare la "stored procedure" senza dover sempre pescare l'utente dal sistema operativo.


(Salvi e riusi in altre parole)


Stilgar


Titolo: Re:Log
Inserito da: Stilgar - Gennaio 03, 2023, 06:20:51 pm
Per migliorare il suggerimento di nomore.
Se duplichi la struttura che devi mettere sotto audit, e aggiungi la colonna utente + operazione, puoi utilizzare 3 valori (quindi aggiungi anche il tipo di manipolazione del record che viene operato).
0=Insert
1=Update
2=Delete
(o una cosa similie).
Così puoi tracciare anche le cancellazioni, oltre che le versioni del record con tutte le modifiche operate sulla singola riga.


Poi, se aggiungi la data inizio validità e fine validità, puoi tenere traccia anche quando è stato modificato il record sotto audit.


Quindi la logica dovrebbe essere:
1) Chiudere la validità del record storicizzato precedente, con il codice di modifica che hai utilizzato + utente, se presente (l'update può lavorare anche se il record no è presente, senza far scattare errori)
Codice: [Seleziona]
update {tabella}_storico set data_validita_fine = CURRENT_TIMESTAMP, mod_code={codice_modifica} where data_validita_fine  is null
una cosa del tipo
2) Modificare/Inserire/Eliminare il record d'interesse.


3) Copiare il record attuale in quello storico

Codice: [Seleziona]
insert {tabella}_storico select *, CURRENT_TIMESTAMP, null, null from {tabella}


Le query sono di esempio, non so se funzionano così come le ho messe (non maledirmi)


Stilgar

Stilgar.
Titolo: Re:Log
Inserito da: cappe - Gennaio 03, 2023, 06:35:22 pm
Ok. Domani provo a farlo.
Titolo: Re:Log
Inserito da: cappe - Gennaio 04, 2023, 11:32:06 am
Per migliorare il suggerimento di nomore.
Se duplichi la struttura che devi mettere sotto audit, e aggiungi la colonna utente + operazione, puoi utilizzare 3 valori (quindi aggiungi anche il tipo di manipolazione del record che viene operato).
0=Insert
1=Update
2=Delete
(o una cosa similie).
Così puoi tracciare anche le cancellazioni, oltre che le versioni del record con tutte le modifiche operate sulla singola riga.
Poi, se aggiungi la data inizio validità e fine validità, puoi tenere traccia anche quando è stato modificato il record sotto audit.

Quindi la logica dovrebbe essere:
1) Chiudere la validità del record storicizzato precedente, con il codice di modifica che hai utilizzato + utente, se presente (l'update può lavorare anche se il record no è presente, senza far scattare errori)
Codice: [Seleziona]
update {tabella}_storico set data_validita_fine = CURRENT_TIMESTAMP, mod_code={codice_modifica} where data_validita_fine  is null
una cosa del tipo
2) Modificare/Inserire/Eliminare il record d'interesse.


3) Copiare il record attuale in quello storico

Codice: [Seleziona]
insert {tabella}_storico select *, CURRENT_TIMESTAMP, null, null from {tabella}


Le query sono di esempio, non so se funzionano così come le ho messe (non maledirmi)


Stilgar

Stilgar.

Grazie.
Fatto e funziona. La cosa interessante è che hai la fotografia della tabella, però come contro ricopia ogni volta tutti i dati. La cosa carina è che è semplice e sembra funzionare e molto probabilmente lo utilizzerò.

Ora provo con solo le modifiche.
Titolo: Re:Log
Inserito da: Stilgar - Gennaio 04, 2023, 02:17:06 pm
Son contento che l'idea ti piaccia.
Tieni conto che ho preso l'approccio più ignorante (o agnostico se vogliamo parlare erudito) possibile.


Un problema di questo approccio è lo spazio.
Ricordati di mettere gli indici anche sulle date validità. Altrimenti con il tempo cercare nello storico diventa un massacro a livello di tempi.


Per le ottimizzazioni del db cedo il passo a Nomore che ha più esperienza di me.


Stilgar.
Titolo: Re:Log
Inserito da: cappe - Gennaio 04, 2023, 02:49:06 pm
ad ogni modo, grazie e una buona idea
Titolo: Re:Log
Inserito da: nomorelogic - Gennaio 04, 2023, 03:30:32 pm
per ora farei una riflessione sulla PK della tabella dello storico

se storicizziamo ad esempio un cliente, questo comparirà N volte nella tabella storico_clienti
per questo se impostiamo la PK come chiave composta con i campi:

avremo il doppio vantaggio di (1) avere la possibilità di memorizzare N volte lo stesso cliente con la data di validità come discriminante e (2) una PK è anche un indice....

resta da capire come fare se la storicizzazione viene lanciata più volte  per lo stesso cliente nello stesso giorno
però la soluzione è semplice: basta che la data di validità sia un timestamp


Titolo: Re:Log
Inserito da: Stilgar - Gennaio 04, 2023, 03:50:57 pm
Infatti nelle query suggerite ho messo current_timestamp().
Solo DATE mi ha creato grosse rogne in passato nella storicizzazione con operazioni parallele.


Stilgar.




Titolo: Re:Log
Inserito da: nomorelogic - Gennaio 04, 2023, 04:20:41 pm
ops... a forza di parlare di "data" avevo dimenticato lo script  ::)
Titolo: Re:Log
Inserito da: cappe - Gennaio 04, 2023, 05:41:08 pm
Penso ci voglia un altro id che fa da chiave univoca.
Titolo: Re:Log
Inserito da: Stilgar - Gennaio 11, 2023, 05:41:06 pm
Continuo a mettere carne al fuoco.


Se vuoi utilizzare il nome utente del sistema operativo per sapere chi ha fatto cosa, prova a vedere questa query:


Codice: [Seleziona]

SELECT DISTINCT a. *
FROM mon$attachments a
JOIN mon$transactions t ON a.mon$attachment_id = t.mon$attachment_id
WHERE NOT (t.mon$read_only = 1 AND t.mon$isolation_mode >= 2)

Forse i campi MON$REMOTE_HOST e MON$OS_USER possono essere interessanti....




Stilgar