Обновление схемы и строк в одной транзакции, SQL Server 2005 - PullRequest
2 голосов
/ 12 сентября 2008

В настоящее время я обновляю устаревшую систему, которая позволяет пользователям определять часть схемы одной из ее таблиц. Пользователи могут создавать и удалять столбцы из таблицы через этот интерфейс. Эта устаревшая система использует ADO 2.8 и использует SQL Server 2005 в качестве своей базы данных (вы даже не хотите знать, какую базу данных она использовала до того, как началась попытка модернизации этого зверя ... но я отвлекся. =))

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

Когда пользователь изменяет список допустимых записей для поля, если он удаляет одно из допустимых значений, ему разрешается выбрать новое «допустимое значение» для сопоставления любых строк, имеющих это (теперь недействительное) значение в нем. , так что теперь они снова имеют действительное значение.

Просматривая старый код, я заметил, что он чрезвычайно уязвим для перевода системы в недопустимое состояние, поскольку упомянутые выше изменения не вносятся в транзакцию (поэтому, если кто-то еще прошел половину процесса, упомянутого выше) и внесли свои изменения ... ну, вы можете себе представить проблемы, которые могут вызвать).

Проблема в том, что я пытался заставить их обновляться в рамках одной транзакции, но всякий раз, когда код попадает в ту часть, где он меняет схему этой таблицы, все остальные изменения (обновление значений в строках, будь то в таблице, где схема изменилась или нет ... они могут быть даже совершенно не связанными таблицами), составленными до этого момента в транзакции, по-видимому, незаметно отброшены. Я не получаю сообщение об ошибке, указывающее, что они были отброшены, и когда я фиксирую транзакцию в конце, ошибка не возникает ... но когда я иду, чтобы посмотреть таблицы, которые должны были быть обновлены в транзакции, только новые столбцы здесь. Ни одно из внесенных изменений, не внесенных в схему, не сохраняется.

Пока что поиск ответов в сети оказался пустой тратой пары часов ... поэтому я обращаюсь за помощью. Кто-нибудь когда-нибудь пытался выполнить транзакцию через ADO, которая одновременно обновляет схему таблицы и обновляет строки в таблицах (будь то та же таблица или другие)? Разве это не разрешено? Есть ли какая-либо документация, которая могла бы помочь в этой ситуации?

EDIT:

Хорошо, я сделал трассировку, и эти команды были отправлены в базу данных (пояснения в скобках)

(я не знаю, что здесь происходит, похоже, он создает временную хранимую процедуру ...?)


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 не было сообщений о том, что во время транзакции что-то пошло не так.

Ответы [ 2 ]

1 голос
/ 12 сентября 2008

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

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

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

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

Обновление:

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

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

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

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

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

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

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

0 голосов
/ 12 сентября 2008

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

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

...