Обновление схемы и строк в одной транзакции, SQL Server 2005

StackOverflow https://stackoverflow.com/questions/57912

Вопрос

В настоящее время я обновляю устаревшую систему, которая позволяет пользователям диктовать часть схемы одной из ее таблиц.Пользователи могут создавать и удалять столбцы из таблицы через этот интерфейс.Эта устаревшая система использует 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 не было сообщений, указывающих на то, что во время транзакции что-то пошло не так.

Это было полезно?

Решение

В коде используется курсор на стороне сервера, вот для чего нужны эти вызовы.Первый набор вызовов готовит/открывает курсор.Затем извлекаем строки из курсора.Наконец закрытие курсора.Эти процедуры аналогичны операторам T-SQL OPEN CURSOR, FETCH NEXT, CLOSE CURSOR.

Мне придется присмотреться (что я и сделаю), но я предполагаю, что что-то происходит с курсором на стороне сервера, инкапсулирующей транзакцией и DDL.

Еще несколько вопросов:

  1. Собираетесь ли вы использовать в этом случае курсоры на стороне сервера?
  2. Все ли команды ADO используют одно и то же активное соединение?

Обновлять:

Я не совсем понимаю, что происходит.

Похоже, вы используете курсоры на стороне сервера, поэтому вы можете использовать Recordset.Update() для отправки изменений обратно на сервер, а также выполнения сгенерированных операторов SQL для изменения схемы и обновления данных в динамических таблицах.Использование того же соединения внутри явной транзакции.

Я не уверен, какое влияние окажут операции с курсором на остальную часть транзакции или наоборот, и, честно говоря, я удивлен, что это не работает.

Я не знаю, насколько значительными будут изменения, но я бы рекомендовал отказаться от курсоров на стороне сервера и создать операторы UPDATE для обновлений таблиц.

Извините, я не смог бы больше помочь.

Кстати, я нашел следующую информацию о вызовах sp_cursor:

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

Другие советы

Поведение, которое вы описываете, разрешено.Как код вносит изменения в схему?Создание SQL на лету и выполнение с помощью команды ADO?Или используете ADOX?

Если у вас есть доступ к серверу базы данных, попробуйте запустить трассировку SQL Profiler при тестировании описанного вами сценария.Посмотрите, регистрирует ли трассировка какие-либо ошибки/откаты.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top