Pergunta

Atualmente estou atualizando um sistema legado que permite aos usuários ditar parte do esquema de uma de suas tabelas.Os usuários podem criar e remover colunas da tabela por meio desta interface.Este sistema legado está usando o ADO 2.8 e o SQL Server 2005 como banco de dados (você nem QUER saber qual banco de dados ele estava usando antes do início da tentativa de modernizar essa fera ...mas estou divagando.=) )

Neste mesmo processo de edição, os usuários podem definir (e alterar) uma lista de valores válidos que podem ser armazenados nestes campos criados pelo usuário (caso o usuário queira limitar o que pode estar no campo).

Quando o usuário altera a lista de entradas válidas para um campo, se remover um dos valores válidos, ele poderá escolher um novo "valor válido" para mapear quaisquer linhas que contenham esse valor (agora inválido), para que eles agora têm um valor válido novamente.

Ao examinar o código antigo, notei que ele é extremamente vulnerável a colocar o sistema em um estado inválido, porque as alterações mencionadas acima não são feitas dentro de uma transação (portanto, se alguém aparecer no meio do processo mencionado acima e fizer sua próprias mudanças...bem, você pode imaginar os problemas que isso pode causar).

O problema é que estou tentando fazer com que eles sejam atualizados em uma única transação, mas sempre que o código chega à parte em que altera o esquema daquela tabela, todas as outras alterações (atualizando valores em linhas, seja em a tabela onde o esquema mudou ou não...elas podem até ser tabelas completamente não relacionadas) feitas até aquele ponto da transação parecem ter sido descartadas silenciosamente.Não recebo nenhuma mensagem de erro indicando que eles foram descartados e, quando eu confirmo a transação no final, nenhum erro é gerado...mas quando vou olhar nas tabelas que deveriam ser atualizadas na transação, apenas as novas colunas estão lá.Nenhuma das alterações feitas fora do esquema é salva.

Procurar respostas na Internet tem sido, até agora, uma perda de algumas horas...então eu volto aqui para obter ajuda.Alguém já tentou realizar uma transação através do ADO que atualizasse o esquema de uma tabela e atualizasse linhas em tabelas (seja essa mesma tabela ou outras)?Não é permitido?Existe alguma documentação que possa ser útil nesta situação?

EDITAR:

Ok, fiz um trace e esses comandos foram enviados para o banco de dados (explicações entre parênteses)

(Não sei o que está acontecendo aqui, parece que está criando um procedimento armazenado temporário...?)


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

(Recuperando a tabela que contém informações de definição para os campos gerados pelo usuário)


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

(Acho que meu código estava percorrendo a lista deles aqui, pegando as informações atuais)


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

(Parece que é aqui que estou inserindo os dados modificados para as definições, analiso cada uma e atualizo todas as alterações que ocorreram nas definições dos próprios campos personalizados)


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

(É aqui que meu código remove os excluídos pela interface antes do início do salvamento]...é também a ÚNICA coisa, pelo que posso dizer, que realmente acontece durante esta transação)


ALTER TABLE CustomizableTable DROP COLUMN _weveknown;

(Agora, se alguma das definições foi alterada de tal forma que as propriedades da coluna criada pelo usuário precisem ser alteradas ou os índices nas colunas precisem ser adicionados/removidos, isso é feito aqui, juntamente com a atribuição de um valor padrão a quaisquer linhas que ainda não tinha um valor para a coluna fornecida ...observe que, até onde eu sei, NADA disso realmente acontece quando o procedimento armazenado termina.)

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

(Fechando a transação...?)

exec sp_cursorclose 180150003
go

Depois de todo esse barulho acima, ocorre apenas a exclusão da coluna.Tudo antes e depois na transação parece ser ignorado e não houve mensagens no SQL Trace para indicar que algo deu errado durante a transação.

Foi útil?

Solução

O código está usando um cursor do lado do servidor, é para isso que servem essas chamadas.O primeiro conjunto de chamadas está preparando/abrindo o cursor.Em seguida, buscando linhas do cursor.Finalmente fechando o cursor.Esses sprocs são análogos às instruções OPEN CURSOR, FETCH NEXT, CLOSE CURSOR T-SQL.

Eu teria que dar uma olhada mais de perto (o que farei), mas acho que há algo acontecendo com o cursor do lado do servidor, a transação de encapsulamento e o DDL.

Mais algumas perguntas:

  1. Você pretende usar cursores do lado do servidor neste caso?
  2. Todos os comandos ADO estão usando a mesma conexão ativa?

Atualizar:

Não tenho certeza do que está acontecendo.

Parece que você está usando cursores do lado do servidor para poder usar Recordset.Update() para enviar alterações de volta ao servidor, além de executar instruções SQL geradas para alterar o esquema e atualizar dados nas tabelas dinâmicas.Usando a mesma conexão, dentro de uma transação explícita.

Não tenho certeza do efeito que as operações do cursor terão no restante da transação, ou vice-versa, e para ser sincero, estou surpreso que isso não esteja funcionando.

Não sei quão grande seria a mudança, mas recomendo afastar-se dos cursores do lado do servidor e criar as instruções UPDATE para as atualizações da tabela.

Desculpe, não pude ajudar mais.

Aliás, encontrei as seguintes informações nas chamadas sp_cursor:

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

Outras dicas

O comportamento que você descreve é ​​permitido.Como o código está fazendo as alterações no esquema?Construindo SQL dinamicamente e executando por meio de um comando ADO?Ou usando ADOX?

Se você tiver acesso ao servidor de banco de dados, tente executar um rastreamento do SQL Profiler enquanto testa o cenário descrito.Veja se o rastreamento registra algum erro/reversão.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top