我目前正在更新一个遗留系统,该系统允许用户指定其表之一的部分架构。用户可以通过此界面在表中创建和删除列。这个遗留系统使用 ADO 2.8,并使用 SQL Server 2005 作为其数据库(在尝试对这个野兽进行现代化改造之前,您甚至不想知道它使用的是哪个数据库......但我离题了。=) )

在同一编辑过程中,用户可以定义(和更改)可存储在这些用户创建的字段中的有效值列表(如果用户想要限制该字段中的内容)。

当用户更改字段的有效条目列表时,如果他们删除了其中一个有效值,则允许他们选择一个新的“有效值”来映射其中包含该值(现在无效)的任何行,以便它们现在再次具有有效值。

在查看旧代码时,我注意到它非常容易使系统进入无效状态,因为上面提到的更改不是在事务中完成的(所以如果其他人在上面提到的过程中途出现并做出了他们的更改)自己的改变...好吧,你可以想象可能会导致的问题)。

问题是,我一直试图让它们在单个事务下更新,但是每当代码到达更改该表架构的部分时,所有其他更改(更新行中的值,无论是在架构更改或未更改的表...它们甚至可以是完全不相关的表),直到事务中的这一点似乎已被悄悄删除。我没有收到任何错误消息,表明它们已被删除,并且当我最后提交事务时,没有引发任何错误......但是当我查看应该在事务中更新的表时,只有新列在那里。所做的任何非架构更改都不会保存。

到目前为止,在网上寻找答案已被证明是浪费几个小时......所以我来这里寻求帮助。有没有人尝试过通过 ADO 执行事务,既更新表的架构又更新表中的行(无论是同一个表还是其他表)?难道不允许吗?在这种情况下是否有任何文档可以提供帮助?

编辑:

好的,我做了跟踪,这些命令被发送到数据库(括号中的解释)

(我不知道这里发生了什么,看起来它正在创建一个临时存储过程......?)


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

(检索保存用户生成字段的定义信息的表)


exec sp_cursoropen @p1 output,N'SELECT * FROM CustomFieldDefs ORDER BY Sequence',@p3 output,@p4 output,@p5 output select @p1, @p3, @p4, @p5
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,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

(这似乎是我输入定义的修改数据的地方,我遍历每个数据并更新自定义字段本身的定义中发生的任何更改)


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

(这是我的代码在保存开始之前通过界面删除已删除的内容的地方]...据我所知,这也是在此交易期间实际发生的唯一事情)


ALTER TABLE CustomizableTable DROP COLUMN _weveknown;

(现在,如果任何定义被更改为需要更改用户创建的列的属性或需要添加/删除列上的索引,则在此处完成,并为任何行提供默认值给定的列还没有值...请注意,据我所知,当存储过程完成时,这些实际上都不会发生。)

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

(结束交易...?)

exec sp_cursorclose 180150003
go

完成上述所有操作后,只发生列的删除。事务中它之前和之后的所有内容似乎都被忽略,并且 SQL 跟踪中没有消息表明事务期间出现问题。

有帮助吗?

解决方案

该代码使用服务器端游标,这就是这些调用的用途。第一组调用正在准备/打开光标。然后从游标中获取行。最后关闭光标。这些存储过程类似于 OPEN CURSOR、FETCH NEXT、CLOSE CURSOR T-SQL 语句。

我必须仔细看看(我会的),但我猜测服务器端游标、封装事务和 DDL 出了问题。

还有一些问题:

  1. 在这种情况下您打算使用服务器端游标吗?
  2. ADO 命令是否都使用相同的活动连接?

更新:

我不太确定发生了什么事。

看起来您正在使用服务器端游标,因此除了执行生成的 SQL 语句来更改架构和更新动态表中的数据之外,您还可以使用 Recordset.Update() 将更改推送回服务器。在显式事务内使用相同的连接。

我不确定游标操作会对事务的其余部分产生什么影响,反之亦然,说实话,我很惊讶这不起作用。

我不知道会有多大的变化,但我建议放弃服务器端游标并为表更新构建 UPDATE 语句。

抱歉我无法提供更多帮助。

顺便说一句-我发现了有关 sp_cursor 调用的以下信息:

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

其他提示

您所描述的行为是允许的。代码如何更改架构?动态构建 SQL 并通过 ADO 命令执行?或者使用ADOX?

如果您有权访问数据库服务器,请尝试在测试您概述的场景时运行 SQL Profiler 跟踪。查看跟踪是否记录任何错误/回滚。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top