Domanda

Attualmente sto aggiornando un sistema legacy che consente agli utenti di dettare parte dello schema di una delle sue tabelle.Gli utenti possono creare e rimuovere colonne dalla tabella tramite questa interfaccia.Questo sistema legacy utilizza ADO 2.8 e utilizza SQL Server 2005 come database (non vuoi nemmeno sapere quale database stava utilizzando prima che iniziasse il tentativo di modernizzare questa bestia...ma sto divagando.=) )

In questo stesso processo di modifica, gli utenti possono definire (e modificare) un elenco di valori validi che possono essere archiviati in questi campi creati dall'utente (se l'utente desidera limitare ciò che può essere nel campo).

Quando l'utente modifica l'elenco di voci valide per un campo, se rimuove uno dei valori validi, può scegliere un nuovo "valore valido" per mappare qualsiasi riga che contiene questo valore (ora non valido), in modo che ora hanno di nuovo un valore valido.

Esaminando il vecchio codice, ho notato che è estremamente vulnerabile a mettere il sistema in uno stato non valido, perché le modifiche menzionate sopra non vengono apportate all'interno di una transazione (quindi se qualcun altro arrivasse a metà del processo sopra menzionato e facesse il suo propri cambiamenti...beh potete immaginare i problemi che potrebbero causare).

Il problema è che ho provato a farli aggiornare con una singola transazione, ma ogni volta che il codice arriva alla parte in cui cambia lo schema di quella tabella, tutte le altre modifiche (aggiornamento dei valori nelle righe, sia in la tabella in cui lo schema è cambiato o meno...possono anche essere tabelle completamente non correlate) create fino a quel momento nella transazione e sembrano essere state eliminate silenziosamente.Non ricevo alcun messaggio di errore che indichi che sono stati eliminati e quando eseguo il commit della transazione alla fine non viene generato alcun errore...ma quando vado a cercare nelle tabelle che avrebbero dovuto essere aggiornate nella transazione, ci sono solo le nuove colonne.Nessuna delle modifiche apportate non allo schema viene salvata.

Cercare risposte in rete si è rivelato, finora, uno spreco di un paio d'ore...quindi mi rivolgo qui per chiedere aiuto.Qualcuno ha mai provato a eseguire una transazione tramite ADO che aggiorni sia lo schema di una tabella che le righe nelle tabelle (che si tratti della stessa tabella o di altre)?Non è consentito?Esiste qualche documentazione che potrebbe essere utile in questa situazione?

MODIFICARE:

Ok, ho eseguito una traccia e questi comandi sono stati inviati al database (spiegazioni tra parentesi)

(Non so cosa stia succedendo qui, sembra che stia creando una procedura memorizzata temporanea ...?)


declare @p1
int set @p1=180150003 declare @p3 int
set @p3=2 declare @p4 int set @p4=4
declare @p5 int set @p5=-1

(Recupero della tabella che contiene le informazioni sulla definizione per i campi generati dall'utente)


exec sp_cursoropen @p1 output,N'SELECT * FROM CustomFieldDefs ORDER BY Sequence',@p3 output,@p4 output,@p5 output select @p1, @p3, @p4, @p5
go

(Penso che il mio codice stesse scorrendo l'elenco qui, catturando le informazioni correnti)


exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursorfetch 180150003,1025,1,1
go
exec sp_cursorfetch 180150003,1028,1,1
go
exec sp_cursorfetch 180150003,32,1,1
go

(Sembra che questo sia il punto in cui sto inserendo i dati modificati per le definizioni, li esamino ciascuno e aggiorno eventuali modifiche apportate alle definizioni per i campi personalizzati stessi)


exec sp_cursor 180150003,33,1,N'[CustomFieldDefs]',@Sequence=1,@Description='asdf',@Format='U|',@IsLookUp=1,@Length=50,@Properties='U|',@Required=1,@Title='__asdf',@Type='',@_Version=1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursor 180150003,33,1,N'[CustomFieldDefs]',@Sequence=2,@Description='give',@Format='Y',@IsLookUp=0,@Length=0,@Properties='',@Required=0,@Title='_give',@Type='B',@_Version=1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursor 180150003,33,1,N'[CustomFieldDefs]',@Sequence=3,@Description='up',@Format='###-##-####',@IsLookUp=0,@Length=0,@Properties='',@Required=0,@Title='_up',@Type='N',@_Version=1
go 
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursor 180150003,33,1,N'[CustomFieldDefs]',@Sequence=4,@Description='Testy',@Format='',@IsLookUp=0,@Length=50,@Properties='',@Required=0,@Title='_Testy',@Type='',@_Version=1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursor 180150003,33,1,N'[CustomFieldDefs]',@Sequence=5,@Description='you',@Format='U|',@IsLookUp=0,@Length=250,@Properties='U|',@Required=0,@Title='_you',@Type='',@_Version=1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursor 180150003,33,1,N'[CustomFieldDefs]',@Sequence=6,@Description='never',@Format='mm/dd/yyyy',@IsLookUp=0,@Length=0,@Properties='',@Required=0,@Title='_never',@Type='D',@_Version=1
go
exec sp_cursorfetch 180150003,32,1,1
go
exec sp_cursor 180150003,33,1,N'[CustomFieldDefs]',@Sequence=7,@Description='gonna',@Format='###-###-####',@IsLookUp=0,@Length=0,@Properties='',@Required=0,@Title='_gonna',@Type='C',@_Version=1
go
exec sp_cursorfetch 180150003,32,1,1
go

(Qui è dove il mio codice rimuove gli elementi eliminati tramite l'interfaccia prima che inizi il salvataggio]...è anche l'UNICA cosa, per quanto ne so, che accade effettivamente durante questa transazione)


ALTER TABLE CustomizableTable DROP COLUMN _weveknown;

(Ora se una qualsiasi delle definizioni è stata modificata in modo tale da dover modificare le proprietà della colonna creata dall'utente o aggiungere/rimuovere gli indici sulle colonne, ciò viene fatto qui, oltre a dare un valore predefinito a qualsiasi riga che non aveva ancora un valore per la colonna specificata...nota che, per quanto ne so, NESSUNO di ciò accade effettivamente al termine della procedura memorizzata.)

go
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'CustomizableTable') AND name = '__asdf'
go
ALTER TABLE CustomizableTable ALTER COLUMN __asdf VarChar(50) NULL
go
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[CustomizableTable]') AND name = N'idx___asdf') CREATE NONCLUSTERED INDEX idx___asdf ON CustomizableTable ( 
__asdf ASC) WITH (PAD_INDEX  = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF);
go
select * from IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[CustomizableTable]') AND name = N'idx___asdf') CREATE NONCLUSTERED INDEX idx___asdf ON 
CustomizableTable ( __asdf ASC) WITH (PAD_INDEX  = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF);
go
UPDATE CustomizableTable SET [__asdf] = '' WHERE [__asdf] IS NULL
go
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'CustomizableTable') AND name = '_give'
go
ALTER TABLE CustomizableTable ALTER COLUMN _give Bit NULL
go
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[CustomizableTable]') AND name = N'idx__give') DROP INDEX idx__give ON CustomizableTable WITH ( ONLINE = OFF );
go
UPDATE CustomizableTable SET [_give] = 0 WHERE [_give] IS NULL
go
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'CustomizableTable') AND name = '_up'
go
ALTER TABLE CustomizableTable ALTER COLUMN _up Int NULL
go
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[CustomizableTable]') AND name = N'idx__up') DROP INDEX idx__up ON CustomizableTable WITH ( ONLINE = OFF );
go
UPDATE CustomizableTable SET [_up] = 0 WHERE [_up] IS NULL
go
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'CustomizableTable') AND name = '_Testy'
go
ALTER TABLE CustomizableTable ADD _Testy VarChar(50) NULL
go
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[CustomizableTable]') AND name = N'idx__Testy') DROP INDEX idx__Testy ON CustomizableTable WITH ( ONLINE = OFF );
go
UPDATE CustomizableTable SET [_Testy] = '' WHERE [_Testy] IS NULL
go
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'CustomizableTable') AND name = '_you'
go
ALTER TABLE CustomizableTable ALTER COLUMN _you VarChar(250) NULL
go
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[CustomizableTable]') AND name = N'idx__you') DROP INDEX idx__you ON CustomizableTable WITH ( ONLINE = OFF );
go
UPDATE CustomizableTable SET [_you] = '' WHERE [_you] IS NULL
go
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'CustomizableTable') AND name = '_never'
go
ALTER TABLE CustomizableTable ALTER COLUMN _never DateTime NULL
go
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[CustomizableTable]') AND name = N'idx__never') DROP INDEX idx__never ON CustomizableTable WITH ( ONLINE = OFF );
go
UPDATE CustomizableTable SET [_never] = '1/1/1900' WHERE [_never] IS NULL
go
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'CustomizableTable') AND name = '_gonna'
go
ALTER TABLE CustomizableTable ALTER COLUMN _gonna Money NULL
go
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[CustomizableTable]') AND name = N'idx__gonna') DROP INDEX idx__gonna ON CustomizableTable WITH ( ONLINE = OFF );
go
UPDATE CustomizableTable SET [_gonna] = 0 WHERE [_gonna] IS NULL
go

(Chiusura della transazione...?)

exec sp_cursorclose 180150003
go

Dopo tutto quanto sopra, avviene solo la cancellazione della colonna.Tutto ciò che precede e segue la transazione sembra essere ignorato e nella traccia SQL non sono presenti messaggi che indichino che qualcosa è andato storto durante la transazione.

È stato utile?

Soluzione

Il codice utilizza un cursore lato server, ecco a cosa servono quelle chiamate.La prima serie di chiamate prepara/apre il cursore.Quindi recuperando le righe dal cursore.Infine chiudendo il cursore.Tali sproc sono analoghi alle istruzioni T-SQL OPEN CURSOR, FETCH NEXT, CLOSE CURSOR.

Dovrei dare un'occhiata più da vicino (cosa che farò), ma suppongo che ci sia qualcosa da fare con il cursore lato server, la transazione di incapsulamento e il DDL.

Altre domande:

  1. Intendi utilizzare i cursori lato server in questo caso?
  2. I comandi ADO utilizzano tutti la stessa connessione attiva?

Aggiornamento:

Non sono esattamente sicuro di cosa stia succedendo.

Sembra che tu stia utilizzando cursori lato server in modo da poter utilizzare Recordset.Update() per inviare nuovamente le modifiche al server, oltre a eseguire istruzioni SQL generate per modificare lo schema e aggiornare i dati nelle tabelle dinamiche.Utilizzando la stessa connessione, all'interno di una transazione esplicita.

Non sono sicuro dell'effetto che avranno le operazioni del cursore sul resto della transazione, o viceversa, e ad essere sincero sono sorpreso che non funzioni.

Non so quanto sarebbe grande il cambiamento, ma consiglierei di allontanarsi dai cursori lato server e di creare le istruzioni UPDATE per gli aggiornamenti della tabella.

Mi spiace, non potrei esserti più d'aiuto.

A proposito, ho trovato le seguenti informazioni sulle chiamate sp_cursor:

http://jtds.sourceforge.net/apiCursors.html

Altri suggerimenti

Il comportamento che descrivi è consentito.In che modo il codice apporta modifiche allo schema?Costruire SQL al volo ed eseguirlo tramite un comando ADO?O usando ADOX?

Se hai accesso al server del database, prova a eseguire una traccia SQL Profiler durante il test dello scenario delineato.Verifica se la traccia registra eventuali errori/rollback.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top