Trigger di inserimento

mercoledì 12 aprile 2006 - 16.35

scandian Profilo | Newbie

ciao.
io avrei questo problema.
dovrei creare un trigger su una vista che viene lanciato quando avviene un inserimento.
il problema è che ogni riga nuova aggiunta sulla vista, il trigger dovrebbe richiamare una funzione che gli restituisce un int, ma deve passare a questa funzione dei parametri ricavati dalle colonne dei nuovi campi inseriti.

in oracle sarebbe cosi

CREATE OR REPLACE TRIGGER WKS33_CTRL_TYPES_BI INSTEAD OF INSERT ON WKS33_VT_CTRL_TYPES
FOR EACH ROW
DECLARE
DescrID SYS10_DICTIONARY.DESCR_ID%TYPE;
BEGIN
DescrID := Sys03_Sequences_Next(:NEW.COMPANY_ID,:NEW.SITE_ID,'SYS10_DICTIONARY_VT','DESCR_ID');
INSERT INTO WKS33_CTRL_TYPES ( COMPANY_ID, SITE_ID, CTRL_TYPE_ID, DESCR_ID, STATUS, TIMESTAMP, USER_ID) VALUES ( :NEW.COMPANY_ID, :NEW.SITE_ID, :NEW.CTRL_TYPE_ID, DescrID, NVL(:NEW.STATUS,'E'), NVL(:NEW.TIMESTAMP,NVL(:NEW.ROW_VERSION,0)), :NEW.USER_ID);
INSERT INTO SYS10_DICTIONARY ( COMPANY_ID, SITE_ID, DESCR_ID, LANGUAGE_ID, DESCRIPTION
, USER_ID)
VALUES ( :NEW.COMPANY_ID,:NEW.SITE_ID, DescrID, :NEW.LANGUAGE_ID_SYS10, :NEW.DESCRIPTION
, :NEW.USER_ID);
END;



ma in sql non ho la possibilità di eseguire il trigger di inserimento come in oracle for each row????

lbenaglia Profilo | Guru

>ma in sql non ho la possibilità di eseguire il trigger di inserimento
>come in oracle for each row????

Se intendi SQL Server la risposta è no.
Prova a postare un *piccolo* esempio in Transact SQL con la struttura delle tabelle (CREATE TABLE), alcune righe di prova (INSERT INTO) ed il risultato che vorresti ottenere, e vedremo di darti una mano.

Ciao!

--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

scandian Profilo | Newbie

praticamente lo script che dovrei fare è quello scritto sopra, potrei farlo con un ciclo per ogni riga inserita, ma non capisco come richiamare n volte la funzione, passandoci i campi delle nuove righe inserite

lbenaglia Profilo | Guru

>praticamente lo script che dovrei fare è quello scritto sopra,

Dato che non conosco il PL/SQL difficilmente potrò aiutarti

>potrei farlo con un ciclo per ogni riga inserita, ma non capisco
>come richiamare n volte la funzione, passandoci i campi delle
>nuove righe inserite

Lavorare row-by-row è la cosa più sbagliata che si possa fare con un RDBMS dato che i db engine sono progettati per offrire le massime prestazioni con operazioni set-based.
Se mi posti l'esempio che ti ho chiesto potremo cercare di individuare insieme una soluzione, altrimenti in bocca al lupo

Ciao!

--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

Ciciu Profilo | Senior Member

Ciao.

Purtroppo è vero... SQLServer ragiona in maniera nettamente differente da Oracle.

Mentre Oracle ci mette a disposizione, for each row, le strutture :new e :old, SQLServer ci mette a disposizione due tabelle virtuali, che sono inserted e deleted.

Queste tabelle possono essere interrogate come qualunque altra tabella ma, ovviamente, NON possono essere aggiornate. Esiste anche la possibilità di effettuare delle join che coinvolgono queste tabelle ed altre tabelle "reali", per esempio per implementare un trigger che controlli l'integrità referenziale prima di una cancellazione.

Tipicamente, per quella che è la mia esperienza, le prime righe da inserire in un trigger di SQLServer sono :

if @@rowcount=0 return

if (select count(*) from inserted)>(select count(*) from deleted)
select @update_type = 'I'
else if (select count(*) from inserted)<(select count(*) from deleted)
select @update_type = 'D'
else if (select count(*) from inserted)=(select count(*) from deleted)
select @update_type = 'U'

La variabile @update_type è un char(1), ed è un surrogato delle booleane "inserted", "updated" e "deleted".

A questo punto, la sola cosa che Ti resta da fare è aprire un cursore sulla inserted (o sulla deleted o sulle due in join, per trappare eventuali update) e, per ogni record, lanciare la Tua procedure e le Tue insert...

declare crsUpd CURSOR local FAST_FORWARD FOR
select isnull(od_parte, '') as ODParte, isnull(od_vers, '') as ODVers
from inserted

open crsUpd
FETCH NEXT FROM crsUpd INTO @ODParte, @ODVers

WHILE @@FETCH_STATUS = 0
begin
-- Uso @ODParte e @ODVers

FETCH NEXT FROM crsUpd INTO @ODParte, @ODVers
end;

if cursor_status('local', 'crsUpd')>=0 close crsUpd
deallocate crsUpd

Altra differenza : i cursori di Oracle sono tipicamente "locali" al trigger. Quelli di SQLServer sono tipicamente "globali". Se un trigger si schianta su un trigger globale, quandorientrerainel trigger lo troverai aperto...

Per evitare casini, io ho sempre usato triggers definiti locali e, soprattutto, definiti FAST FORWARD.

Spero Ti sia utile.

Ciao - Fabio
Fabio G

lbenaglia Profilo | Guru

>Tipicamente, per quella che è la mia esperienza, le prime righe
>da inserire in un trigger di SQLServer sono :
>
> if @@rowcount=0 return
>
>if (select count(*) from inserted)>(select count(*) from deleted)
> select @update_type = 'I'
>else if (select count(*) from inserted)<(select count(*) from
>deleted)
> select @update_type = 'D'
>else if (select count(*) from inserted)=(select count(*) from
>deleted)
> select @update_type = 'U'

E' possibile definire singolarmente uno o più trigger di insert, delete ed update rendendo superflui quei controlli.

>A questo punto, la sola cosa che Ti resta da fare è aprire un
>cursore sulla inserted (o sulla deleted o sulle due in join,
>per trappare eventuali update) e, per ogni record, lanciare la
>Tua procedure e le Tue insert...

Perché?
E' possibile scrivere un singolo comando di INSERT (o UPDATE o DELETE) senza la necessità di definire cursori, semplicemente facendo le dovute JOIN con le tabelle virtuali INSERTED e DELETED.

>Altra differenza : i cursori di Oracle sono tipicamente "locali"
>al trigger. Quelli di SQLServer sono tipicamente "globali".

Questo era vero fino alla versione 7.0 di SQL Server.
I BOL recitano: "Note If neither GLOBAL or LOCAL is specified, the default is controlled by the setting of the default to local cursor database option. In SQL Server version 7.0, this option defaults to FALSE to match earlier versions of SQL Server, in which all cursors were global".
http://msdn.microsoft.com/library/en-us/tsqlref/ts_de-dz_31yq.asp

Ciao!

--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

Ciciu Profilo | Senior Member

>>Tipicamente, per quella che è la mia esperienza, le prime righe
>>da inserire in un trigger di SQLServer sono :
>>
>> if @@rowcount=0 return
>>
>>if (select count(*) from inserted)>(select count(*) from deleted)
>> select @update_type = 'I'
>>else if (select count(*) from inserted)<(select count(*) from
>>deleted)
>> select @update_type = 'D'
>>else if (select count(*) from inserted)=(select count(*) from
>>deleted)
>> select @update_type = 'U'
>
>E' possibile definire singolarmente uno o più trigger di insert,
>delete ed update rendendo superflui quei controlli.

Hai perfettamente ragione. Ma, per logiche simili, è (spesso) più manutenibile un trigger che si occupa dei tre aggiornamenti, piuttosto che tre trigger... Se sei costretto a fare una modifica alla logica di funzionamento, Ti ritroveresTi a doverla replicare su 3 trig. Ovviamente questo è un discorso impossibile da generalizzare : di volta in volta bisogna verificare la strada migliore...

>
>>A questo punto, la sola cosa che Ti resta da fare è aprire un
>>cursore sulla inserted (o sulla deleted o sulle due in join,
>>per trappare eventuali update) e, per ogni record, lanciare la
>>Tua procedure e le Tue insert...
>
>Perché?
>E' possibile scrivere un singolo comando di INSERT (o UPDATE
>o DELETE) senza la necessità di definire cursori, semplicemente
>facendo le dovute JOIN con le tabelle virtuali INSERTED e DELETED.

Anche in questo caso, è necessario verificare caso per caso... E' possibile che esistano (ed esistono, credimi !) casi in cui non riesci a ricondurre la logica di un trigger ad una "banale" istruzione SQL... Certo è che nei casi in cui è possibile ricondurre il tutto ad una serie di comandi SQL legati a queste tabelle virtuali, le performances del trigger ne beneficiano enormemente...


>
>>Altra differenza : i cursori di Oracle sono tipicamente "locali"
>>al trigger. Quelli di SQLServer sono tipicamente "globali".
>
>Questo era vero fino alla versione 7.0 di SQL Server.
>I BOL recitano: "Note If neither GLOBAL or LOCAL is specified,
>the default is controlled by the setting of the default to local
>cursor database option. In SQL Server version 7.0, this option
>defaults to FALSE to match earlier versions of SQL Server, in
>which all cursors were global".
>http://msdn.microsoft.com/library/en-us/tsqlref/ts_de-dz_31yq.asp

Devo darTi una triste notizia... L'installazione che ho in locale di SQLServer 2005 (Developer Edition) è assolutamente standard (non conoscendo benissimo il prodotto dal punto di vista strettamente sistemistico ho fatto l'installazione in modalità "Next,Next,Next") : ho subito verificato la configurazione dei cursori sul database e.... Default Cursor è su GLOBAL !!! Del resto, a pensarci bene, è quello che dicono i BOL : se nella dichiarazione del cursore non specifichi alcuno scope, viene preso il default del database (GLOBAL, per l'appunto). Solo nella versione 7, l'assenza di modificatori veniva presa come FALSE, poiché l'impostazione a libello di DB era probabilmente differente, in quanto richiedeva True/False per determinare se utilizzare cursori globali. Questa, almeno, è la mia interpretazione di quanto hai riportato...


Questo
>
>Ciao!

Cia'
Fabio

>
>--
>Lorenzo Benaglia
>Microsoft MVP - SQL Server
>http://blogs.dotnethell.it/lorenzo/
>http://italy.mvps.org

Fabio G
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5