Newsgroup: it.comp.software.database
Titolo: Problemone con una tabella formata da join di una view con una seconda tabella

Ciao a tutti,
premesso che le mie competenze di SQL & Affini sono basilari, vado a 
esporvi lo scenario in cui mi sto muovendo e i miei problemi.
ho una tabella Impiegato preesistente nel db di un cliente in cui sono 
persistenti Nome, Cognome, Matricola (chiave primaria della tabella) e 
NumeroDiTelefono di tutti i dipendenti. Per una applicazione che sto 
sviluppando per il suddetto cliente, avrei bisogno di *estendere* la 
tabella, inserendo un campo "Status".
Nel mio db ho creato quindi una vista ViewImpiegato [1] che altro non fa 
che una select di tutti gli elementi di Impiegato. Poi ho creato una 
tabella EstensioneImpiegato che ha due colonne: Matricola e Status. 
Infine una tabella FullImpiegato ottenuta dalla JOIN tra ViewImpiegato e 
EstensioneImpiegato.
Ora iniziano i problemi. :-)
1) Mentre i campi di ViewImpiegato sono (ovviamente) read-only, il campo 
"Status" può essere scritto, tuttavia la tabella FullImpiegato può 
essere aggiornata in SOLO in UPDATE e SOLO per il campo "Status". E' 
possibile? A occhio non ci dovrebbero essere problemi... :-)
2) Il problema ce lo ho nella sincronizzazione degli Impiegati. Ad 
esempio, se limpiegato con matricola 17 va in pensione, il record viene 
rimosso dal db originale, io non lo vedo più in ViewImpiegato e la riga 
che avevo in sua corrispondenza in EstensioneImpiegato non viene più 
coinvolta nella Join (poco male, al limite avrò una riga in più che 
occupa spazio :-)). Il problema è quando un nuovo impiegato viene 
inserito. Esiste un modo per far si che se in EstensioneImpiegato non 
esiste alcuna riga che matcha una riga della tabella in Join venga 
CREATA AL VOLO con valori di default per i vari campi?
3) in riferimento alla domanda 2: esiste qualche altro metodo per 
sincronizzare le mie "estensioni" con il db del cliente, senza dover 
ricorrere a trigger?
Ciao e grazie in anticipo,
Giulio
[1] per la precisione: sono su un SQL Server 2008 e accedo al db 
"legacy" del cliente mediante una openquery su linkedserver.
-- 

 [...]
Ciao  Manlio

 Ora iniziano i problemi. :-)
Non capisco perche' tu abbia fatto/dovuto fare in questo modo, quando
nel 90% dei casi basta una "alter table impiegato add ( stato
char(1) ); /* Oracle */".
Un altro 9% comprende tutti i casi in cui negli applicativi vengono
fatti contemporaneamente delgli static bindings e delle "select *", e
l' altro 1% tutti i casi in cui vi siano problemi "organizzativi", ma
credo che, vista la tua frase introduttiva, non siano i casi che stai
vedendo.

 char(1) ); /* Oracle */".
Quindi io vado da un cliente che mi da accesso in sola lettura a una 
sola tabella del suo db tramite un linkedserver e gli dico: "ciao, mi 
fai fare una bella alter sulla tabella X?". Cioè... a livello "politico" 
la strada è scarsamente percorribile. :-)
Ciao,
Giulio
-- 

 Che io sappia solo con un trigger.
Ma sarebbe un trigger sulla insert/delete del db "legacy", giusto?
Ecco, io quel db non posso toccarlo. :-(
Ciao,
Giulio
-- 

 Ma sarebbe un trigger sulla insert/delete del db "legacy", giusto?
Si, perchè assumo ti serva per compatibilità con vecchio codice che 
inserisce i dati in quella tabella.
Se il problema è questo, non vedo altre soluzioni fattibili.
Se non è un problema (nel senso che il codice che inserisce nuovi 
impiegati lo puoi modificare), allora ti basta fare una insert nella 
vista EstensioneImpiegato e lavorare qui per far funzionare il tutto.
Ciao  Manlio

 vista EstensioneImpiegato e lavorare qui per far funzionare il tutto.
Alla fine ho prototipato una soluzione del genere:
1) la mia tabella FullEmployee è una vista formata dalla OUTER JOIN 
della tabella "legacy" con la tabella ExtensionEmployee.
2) quando un record legacy non trova una corrispondenza con un record 
della mia tabella (perché, ad esempio, è stato inserito dopo), la OUTER 
JOIN crea un record con dei valori NULL. Nel dominio applicativo, questi 
record saranno entità con valori di default per le colonne NULL.
3) se viene richiamata una UPDATE su uno di questi record "volanti", un 
trigger sulla INSERT/UPDATE nella view mi genera al volo il record 
corrispondente nella mia tabella.
Ciao,
Giulio
-- 

 
L'update sulla vista FullImpiegato non è banale, utilizzando la mia 
soluzione, perchè non sai se lo stato è presente o no.
La soluzione *sembra* essere MERGE:
http://en.wikipedia.org/wiki/Merge_(SQL)
Ciao  Manlio

 
http://en.wikipedia.org/wiki/Merge_(SQL)
Grazie del suggerimento: comincioa dargli uno sguardo e a fare qualche 
prova.
Grazie ancora,
Giulio
-- 

 
http://en.wikipedia.org/wiki/Merge_(SQL)
Cioè... tu dici di usare un MERGE dentro al trigger?
Ciao e grazie,
Giulio
-- 



Si, il trigger sull'UPDATE.
Serve quando fai
UPDATE FullImpiegato SET Stato = <stato> WHERE Matricola = <matricola>
**se** vuoi gestire il caso in cui un impiegato non abbia ancora lo stato 
assegnato esplicitamente.
La soluzione con COALESCE gestisce il caso in cui lo Stato non sia ancora 
stato assegnato esplicitamente, in una query SELECT.
Ciao  Manlio

 assegnato esplicitamente.
Scusa la niubbaggine ma... io ho un trigger che ho creato così:
CREATE TRIGGER sincro_extension
ON FullImpiegato
INSTEAD OF INSERT, UPDATE
AS
IF (SELECT COUNT(*) FROM ExtensionImpiegato WHERE Id = (SELECT Id FROM 
inserted)) = 0
BEGIN
INSERT INTO ExtensionImpiegato (Id, Status) VALUES ((SELECT Id FROM 
inserted), (SELECT Status FROM INSERTED))
END
ELSE
BEGIN
UPDATE ExtensionImpiegato SET Status = (SELECT Status FROM inserted) 
WHERE Id = (SELECT Id FROM Inserted)
END
Che differenza c'è tra una soluzione del genere e una che utilizzi il merge?


 
Trovi la sintassi e degli esempi di MERGE per MSSQL.
http://msdn.microsoft.com/en-us/library/bb510625.aspx
Tra l'altro trovi anche una implementazione alternativa del tuo codice 
precedente, che prima fa prima un update, e controlla se abbia 
effettivamente aggiornato delle righe (questo è il metodo che uso in una 
applicazione, con PostgreSQL, con la differenza che il controllo è fatto 
nell'applicazione)
Una prima differenza è che MERGE è un singolo statement, quindi 
l'istruzione, oltre ad essere più semplice, è "naturalmente" atomica. 
Anche il tuo esempio è atomico, ma solo perchè eseguito in una 
transazione.
Date ha un pò da dire tra la differenza tra atomicità a livello di 
singolo statement e a livello di transazioni, anche se io non sono sicuro 
di capire a fondo il suo discorso.
Ciao  Manlio