Allineamento dati tra una tabella locale ed una tabella collegata da u...

venerdì 15 maggio 2009 - 20.12

palex Profilo | Newbie

Buongiorno.
Ho un problema di questo tipo: vorrei gestire all'interno di un database SQL-SERVER (uso la versione 2005) anche i dati presenti su un sistema esterno (altro db e altra struttura dati) e ho dei dubbi su quale strada sia meglio seguire.
Il caso è (per fortuna) limitato ad alcune tabelle anagrafiche.
Andiamo con ordine.
Sul mio DB SQL ho creato un "server collegato" al db esterno mediante provider MSDASQL (l'unico servizio che il db esterno riesce a darmi è una connessione ODBC). In questo modo riesco a vedere le tabelle presenti sul db esterno e, parametrizzando opportunamente il provider, riesco anche a effettuare istruzioni di INSERT, UPDATE e DELETE sulla tabella esterna, avendo l'accortezza di chiamare la tabella con la corretta sintassi.
Ora facciamo un esempio con la tabella "Gestori".
Sul sistema gestionale esterno, esiste una tabelle "Gestori" (la chiameremo GE) con due soli campi (codice [int] e descrizione [char](30). Codice è anche PK).
Sul mio db esiste una tabella omonima "Gestori" (la chiameremo GI) che contiene, oltre ai campi "codice" e "descrizione", anche una serie di altre informazioni che l'utente vuole gestire e che non puo' collocare direttamente sul db esterno.
Inoltre, la codifica del db esterno vede il gestore classificato come informazione numerica, mentre l'utente vuole ricodificare i gestori secondo una classificazione alfanumerica.
La mia tabella locale pertanto è fatta nel seguente modo:
CREATE TABLE [dbo].[Gestori](
[codice] [char](10) NOT NULL,
[descrizione] [varchar](30) NULL,
[qualifica] [varchar](50) NULL,
[... altri campi non significativi ... ]
[codSistemaEsterno] [int] NOT NULL,
CONSTRAINT [PK_Gestori] PRIMARY KEY CLUSTERED ([codice] ASC) ON [PRIMARY]
) ON [PRIMARY]

Infine, questa tabella è stata prepopolata da una vista che ha estratto i dati dalla tabella GE
CREATE VIEW [dbo].[WE_gestori]
AS
SELECT GE.reo_responsabile AS codice,
GE.reo_descr AS descrizione,
GE.reo_responsabile AS codSistemaEsterno
FROM DBEXT..root.gestori AS GE

facendo coincidere le codifiche.

Successivamente l'utente ha provveduto a modificare i codici di GI, trasformandoli in codici alfanumerici.
Un estratto della tabella odierna riporta quindi i seguenti valori:

codice descrizione qualifica codiceSistemaEsterno
ab         Alessandro Bianchi gruista 3
ar         Alessandro Rossi op. edile 5
gv         Giuseppe Verdi capo cantiere 12
mr         Mario Rossi responsabile 23
...

Ora, il problema.
L'utente vorrebbe evitare di tenere allineate DUE tabelle (GE e GI) ogni volta che deve fare una modifica a questa tabella.
E' disponibile a gestire i dati su GI (anche perchè sono più completi) e accettare il fatto che si crei automaticamente un record su GE.
Chiaramente, l'operazione "si puo' fare", anche se non posso creare lock o transazioni sul db esterno (ma posso dare per scontato che - non gestendo più la tabella GE - non vi siano istanze aperte in modifica e le uniche azioni siano SELECT in lettura sul db esterno)...
Sono indeciso se seguire una strada "tutta via T-SQL", creando un trigger e gestendo l'eventi AFTER INSERT, creando d'ufficio un record sulla tabella esterna (non so bene come calcolare un codice nuovo numerico, esiste una funzione Hash()? oppure, potrei inserire un autoincrement con start da 1000 (cosi' non vado in collisione con i valori esistenti)... Sono indeciso se gestire AFTER UPDATE (l'unica modifica è la descrizione) e non gestirei proprio l'eliminazione, bloccandone l'operatività (non ho modo di risolvere l'integrità referenziale sul db esterno, per cui l'utente gestirà questa casistica eventuale - e remota - direttamente sul sistema esterno).
In alternativa, potrei gestire tutto "via codice" dalla applicazione che gestisce la tabella GI (attualmente usa VB .NET su una maschera di Visual Studio). Anche se preferirei cercare di limitare l'azione al DB, senza intervenire sul codice VB.
Infine, la domanda vera: è la prima volta che uso un trigger su SQL server... per cui non so bene "come" pilotare questa azione. Soprattutto, non so bene come gestire eventuali situazioni di anomalia (posso ad esempio attivare una rollback dal trigger che ritorni l'errore alla procedura di gestione della tabella GI)?

Grazie in anticipo
--
Alessandro Pisano

lbenaglia Profilo | Guru

>Soprattutto, non so bene come gestire eventuali situazioni di
>anomalia (posso ad esempio attivare una rollback dal trigger
>che ritorni l'errore alla procedura di gestione della tabella
>GI)?

Ciao Alessandro,

Secondo me con uno o più after triggers risolvi il problema di allineamento tra le due tabelle.
Un after trigger DML viene richiamato successivamente all'operazione che l'ha scatenato (INSERT, UPDATE o DELETE) lasciando aperta la transazione fino al termine del trigger stesso, pertanto una operazione di ROLLBACK nel body del trigger non farà altro che annullare tutte le modifiche apportate sia dal trigger che dall'operazione scatenante.
Per rilanciare un errore al client dopo aver eseguito la ROLLBACK TRAN puoi ricorrere alla funzione RAISERROR.
Inoltre sui Books Online dai un'occhiata all'utilizzo dei costrutti TRY…CATCH.

>Grazie in anticipo
Prego.

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

palex Profilo | Newbie

Ciao Lorenzo,
ho provato a seguire i tuoi suggerimenti, ma ho un problema di aggiornamento che non riesco a capire.
Ho fatto la prova su una tabella più semplice, in cui il codice è comunque alfanumerico.
Questa è la situazione:

1. database collegato: DBEXT con provider MSDASQL
(il provider ha il parametro NonTransactedUpdates fissato a True)

2. tabella esterna DBEXT..port.zon
una VIEW ci consente di analizzare i dati presenti
CREATE VIEW [dbo].[WX_zone] AS SELECT * FROM DBEXT..port.zon
e ci consente di vedere le dimensioni dei campi sul sistema esterno
INSERT INTO [prova].[dbo].[WX_zone] ([zn_sigla] ,[zn_descrizione] ,[zn_ragg_1] ,[zn_ragg_2]) VALUES (<zn_sigla, char(8),> ,<zn_descrizione, char(30),> ,<zn_ragg_1, char(3),> ,<zn_ragg_2, char(3),>)

3. tabella locale Zone, per il momento costruita in modo semplificato
CREATE TABLE [dbo].[Zone]( [codice] [char](10) NOT NULL, [descrizione] [varchar](50) NOT NULL, [codSE] [char](8) NULL, CONSTRAINT [PK_Zone] PRIMARY KEY CLUSTERED ([codice] ASC) ON [PRIMARY] ) ON [PRIMARY]
si noti che la dimensione del codice è diversa.

4. ora, il trigger:
Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

5. a questo punto, ho provato a inserire una riga sulla tabella Zone
codice descrizione codSE
123        zona di prova NULL

Il trigger scatta, ma ritorna un errore nella fase di inserimento:
Errore: 7390 Impossibile eseguire l'operazione richiesta perchè il provider OLE DB "MSDASQL" per il server collegato "DBEXT" non supporta l'interfaccia di transazione necessaria.

Questo errore in verità dovrebbe essere "gestito" proprio dal parametro NonTransactedUpdates, appunto volutamente modificato a True.
Peraltro, come controprova, dopo aver disabilitato il trigger, ho inserito a mano un elemento nella tabella e successivamente, da query interattiva, ho eseguito il seguente comando di INSERT (analogo al trigger):
DECLARE @codSE CHAR(8) SET @codSE = CAST((SELECT codice FROM dbo.Zone) AS char(8)) INSERT INTO DBEXT..port.zon (zn_sigla, zn_descrizione, zn_ragg_1, zn_ragg_2) SELECT CAST(@codse AS char(8)), CAST(I.descrizione AS char(30)), 'AUT', '' FROM dbo.Zone AS I GO
In questo caso, tutto funziona correttamente (la risposta in esecuzione della query è "(Righe interessate: 1)" e una analisi dei dati tramite la vista WX_zone mi fa vedere il nuovo record inserito.

Dov'è l'inghippo?

Grazie fin d'ora per l'attenzione.
--
Alessandro Pisano

palex Profilo | Newbie

Dopo diverse prove effettuate, purtroppo devo scartare la soluzione del Trigger, perchè il DB esterno non supporta la modalità "Aggiornamento in Transazioni", ma questa a quanto pare è "implicita" nella esecuzione del trigger stesso, anche disabilitandola sul provider utilizzato per la connessione della fonte dati esterna.
Ho risolto (apparentemente) il problema scrivendo il codice di aggiornamento remoto in una STORE PROCEDURE (che a quanto pare è invece sensibile al parametro del provider, al contrario del Trigger).
Purtroppo questo mi costringe a modificare il codice VB... cosa che non avrei voluto fare.

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