Pregunta

Actualmente estoy actualizando un sistema heredado que permite a los usuarios dictar parte del esquema de una de sus tablas.Los usuarios pueden crear y eliminar columnas de la tabla a través de esta interfaz.Este sistema heredado utiliza ADO 2.8 y SQL Server 2005 como base de datos (ni siquiera QUIERES saber qué base de datos estaba usando antes de que comenzara el intento de modernizar esta bestia...pero yo divago.=) )

En este mismo proceso de edición, los usuarios pueden definir (y cambiar) una lista de valores válidos que se pueden almacenar en estos campos creados por el usuario (si el usuario desea limitar lo que puede haber en el campo).

Cuando el usuario cambia la lista de entradas válidas para un campo, si elimina uno de los valores válidos, se le permite elegir un nuevo "valor válido" para asignar cualquier fila que tenga este valor (ahora no válido), de modo que ahora tienen un valor válido nuevamente.

Al revisar el código antiguo, noté que es extremadamente vulnerable a poner el sistema en un estado no válido, porque los cambios mencionados anteriormente no se realizan dentro de una transacción (por lo que si alguien más llega a la mitad del proceso mencionado anteriormente y hace su propios cambios...bueno, os podéis imaginar los problemas que puede causar).

El problema es que he estado intentando que se actualicen en una sola transacción, pero cada vez que el código llega a la parte donde cambia el esquema de esa tabla, todos los demás cambios (actualizando valores en filas, ya sea en la tabla donde el esquema cambió o no...(incluso pueden ser tablas completamente no relacionadas) creadas hasta ese punto de la transacción parecen eliminarse silenciosamente.No recibo ningún mensaje de error que indique que se eliminaron y, cuando al final realizo la transacción, no se genera ningún error...pero cuando voy a buscar en las tablas que se suponía que debían actualizarse en la transacción, solo aparecen las nuevas columnas.Ninguno de los cambios realizados fuera del esquema se guarda.

Hasta ahora, buscar respuestas en la red ha resultado ser una pérdida de un par de horas...así que acudo aquí en busca de ayuda.¿Alguien ha intentado alguna vez realizar una transacción a través de ADO que actualice el esquema de una tabla y actualice las filas de las tablas (ya sea la misma tabla u otras)?¿No está permitido?¿Existe alguna documentación que pueda ser útil en esta situación?

EDITAR:

Bien, hice un seguimiento y estos comandos se enviaron a la base de datos (explicaciones entre paréntesis)

(No sé qué está pasando aquí, ¿parece que se está creando un procedimiento almacenado temporal...?)


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 la tabla que contiene información de definición para los campos generados por el usuario)


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

(Creo que mi código estaba recorriendo la lista de ellos aquí, obteniendo la información actual)


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

(Aquí parece ser donde ingreso los datos modificados para las definiciones, reviso cada uno y actualizo cualquier cambio que haya ocurrido en las definiciones de los 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

(Aquí es donde mi código elimina lo eliminado a través de la interfaz antes de que comenzara este guardado]...también es lo ÚNICO, hasta donde sé, que realmente sucede durante esta transacción)


ALTER TABLE CustomizableTable DROP COLUMN _weveknown;

(Ahora, si alguna de las definiciones se modificó de tal manera que sea necesario cambiar las propiedades de la columna creada por el usuario o agregar/eliminar índices en las columnas, se hace aquí, además de dar un valor predeterminado a cualquier fila que aún no tenía un valor para la columna dada...tenga en cuenta que, hasta donde yo sé, NADA de esto sucede cuando finaliza el procedimiento almacenado).

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

(¿Cerrar la transacción...?)

exec sp_cursorclose 180150003
go

Después de todo el ruido anterior, sólo se produce la eliminación de la columna.Todo lo anterior y posterior en la transacción parece ignorarse y no hubo mensajes en el seguimiento SQL que indiquen que algo salió mal durante la transacción.

¿Fue útil?

Solución

El código utiliza un cursor del lado del servidor, para eso sirven esas llamadas.El primer conjunto de llamadas está preparando/abriendo el cursor.Luego, recupera filas del cursor.Finalmente cerrando el cursor.Esos sprocs son análogos a las declaraciones T-SQL OPEN CURSOR, FETCH NEXT, CLOSE CURSOR.

Tendría que mirar más de cerca (lo cual haré), pero supongo que algo está sucediendo con el cursor del lado del servidor, la transacción de encapsulación y el DDL.

Algunas preguntas más:

  1. ¿Quiere utilizar cursores del lado del servidor en este caso?
  2. ¿Todos los comandos ADO utilizan la misma conexión activa?

Actualizar:

No estoy exactamente seguro de lo que está pasando.

Parece que está usando cursores del lado del servidor, por lo que puede usar Recordset.Update() para enviar los cambios al servidor, además de ejecutar declaraciones SQL generadas para alterar el esquema y actualizar los datos en las tablas dinámicas.Usando la misma conexión, dentro de una transacción explícita.

No estoy seguro de qué efecto tendrán las operaciones del cursor en el resto de la transacción, o viceversa, y para ser honesto, me sorprende que esto no esté funcionando.

No sé qué tan grande sería el cambio, pero recomendaría alejarse de los cursores del lado del servidor y crear las declaraciones UPDATE para las actualizaciones de su tabla.

Lo siento, no pude ser de más ayuda.

Por cierto, encontré la siguiente información en las llamadas a sp_cursor:

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

Otros consejos

El comportamiento que usted describe está permitido.¿Cómo cambia el código el esquema?¿Construir SQL sobre la marcha y ejecutarlo a través de un comando ADO?¿O usar ADOX?

Si tiene acceso al servidor de la base de datos, intente ejecutar un seguimiento del Analizador SQL mientras prueba el escenario que describió.Vea si el seguimiento registra algún error o reversión.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top