in

DotNetSide

Dot Net South Italy Developers User Group

Tips

Logiche di business mediante trigger

Autore: Francesco Quaratino

I trigger sono, talvolta, l'estremo rimedio a problemi altrimenti insormontabili. Anche se Microsoft tende a scoraggiarne l'uso, in alcuni casi è bene prenderli in seria  considerazione, come, per esempio, l'implementazione di logiche aziendali complesse che l'integrità referenziale non è in grado di supportare.

Questo tips presenta un caso concreto in cui l'uso dei trigger favorisce le performance di un'applicazione. Mi riferisco a un'applicazione gestionale abbastanza comune, legata alla gestione acquisti di un prodotto: il calcolo dell'ultimo costo d'acquisto del prodotto.

Conoscere il costo ultimo di un prodotto è di fondamentale importanza per riordinarlo dal fornitore più conveniente, così come per fissarne un prezzo di vendita.

Spesso i progettisti software decidono di far calcolare il costo ultimo nel momento stesso in cui viene richiesto dall'utente, motivando tale scelta col fatto che si tratta di un “campo calcolabile”, il quale non necessità di essere ospitato permanentemente nel database (e di conseguenza trattato in fase di inserimento/modifica/cancellazione).

Se, però, questa decisione non è supportata da un'analisi preventiva del carico di lavoro a cui sarà soggetta l'applicazione, la procedura che si occupa di calcolare il costo ultimo, rischia di diventare il collo di bottiglia di funzionalità vitali della nostra applicazione (come, per esempio, la fase di riordino dei prodotti la cui giacenza va sotto la soglia della scorta minima  prestabilita, che in alcune realtà commerciali è un'operazione svolta giornalmente e molto onerosa da un punto di vista delle risorse di sistema impegnate).

Supponiamo quindi di trovarci di fronte al seguente database, che raccoglie gli ordini a fornitore di prodotti di natura non specificata:





Per l'esattezza, ecco i significati delle tabelle:

●    [OrderHeader]: i dati generali degli ordini a fornitore
●    [OrderDetail]: i dati dettagliati dei prodotti ordinati
●    [Fornitore]: i dati dei fornitori che ci riforniscono i nostri depositi
●    [Prodotto]: i dati dei prodotti ordinabili ai ns fornitori

Con questi dati a disposizione, ricavare - mediante Transact-SQL - il prezzo ultimo del prodotto per un particolare fornitore, significa scrivere questo codice:

1    DECLARE 
2 @codice_prodotto INT,
3 @codice_fornitore INT
4
5 SET
@codice_fornitore = 1
6 SET @codice_prodotto = 3
7
8
9 SELECT TOP 1
10 prezzo_acquisto
11 FROM
12 OrderHeader H
13 INNER JOIN
14 OrderDetail D
15 ON
16 D.idHeader = H.id
17 WHERE
18 codice_prodotto = @codice_prodotto
19 AND
20 codice_fornitore = @codice_fornitore
21 ORDER BY
22 data_ordine
23 DESC


Questa query risolve il problema restituendo il dato desiderato, ma in presenza di migliaia di ordini tale risultato potrebbe giungere in tempi troppo lunghi per la pazienza di utente. Proviamo a risolvere l'eventuale problema di performance “materializzando” il campo calcolabile del prezzo ultimo, attraverso la tabella [CostoUltimoProdottoFornitore]:





Quindi gestiamo la nuova tabella attraverso il seguente trigger creato sulla tabella [OrderDetail] che scatta in fase di inserimento e modifica:

1    CREATE TRIGGER tr_insert_update_costo_ultimo
2 ON dbo.OrderDetail
3 AFTER
4 INSERT, UPDATE
5 AS
6
7 IF
@@ROWCOUNT = 0
8 RETURN
9
10 /* verifico l'esistenza nella tabella [CostoUltimoProdottoFornitore] della riga
11 relativa al prodotto inserito o aggiornato nell'ordine */

12 IF EXISTS
13 (
14 SELECT *
15 FROM CostoUltimoProdottoFornitore C
16 INNER JOIN Inserted i
17 ON C.codice_prodotto = i.codice_prodotto
18 INNER JOIN OrderHeader H
19 ON H.id = i.idHeader
20 AND H.codice_fornitore = C.codice_fornitore
21 )
22 /* ne aggiorno il costo ultimo */
23 UPDATE C
24 SET costo_ultimo = i.prezzo_acquisto
25 FROM CostoUltimoProdottoFornitore C
26 INNER JOIN Inserted i
27 ON C.codice_prodotto = I.codice_prodotto
28 INNER JOIN OrderHeader H
29 ON H.id = i.idHeader
30 AND H.codice_fornitore = C.codice_fornitore
31 ELSE
32 /* inserisco la riga corrispondete */
33 INSERT INTO CostoUltimoProdottoFornitore
34 ( codice_prodotto, codice_fornitore, costo_ultimo)
35 SELECT i.codice_prodotto, H.codice_fornitore, i.prezzo_acquisto
36 FROM Inserted i
37 INNER JOIN OrderHeader H
38 ON H.id = i.idHeader
39
40 RETURN
41 GO


In realtà, la gestione della tabella  [CostoUltimoProdottoFornitore] non si esaurisce con questo trigger. In genere, infatti, occorrerebbe trattare l'eventuale cancellazione del prodotto dall'ordine, così come la modifica del fornitore dell'ordine e tutte gli altri eventi che le applicazioni costruite sul database sono in grado di attivare. Potrebbe essere opportuno anche strutturare diversamente la tabella in modo da conservare un numero ragionevole di costi ultimi e non uno soltanto come nel nostro esempio, la cui complessità è stata volutamente mantenuta ridotta.

N.B.: scarica lo script allegato per generare il database e il trigger di esempio.

 
 
  


 
Only published comments... Jul 12 2006, 08:57 AM by VitoA
Filed under:
Attachment: Script_1.zip
Powered by Community Server (Commercial Edition), by Telligent Systems