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


Описанное вами поведение разрешено. Как код меняет схему? Создание SQL на лету и выполнение с помощью команды ADO? Или с помощью ADOX?
Если у вас есть доступ к серверу базы данных, попробуйте запустить трассировку SQL Profiler при тестировании описанного вами сценария. Посмотрите, регистрирует ли трассировка какие-либо ошибки / откаты.
Ладно, воспользовался трассировкой, правда ничего необычного не увидел. = (Я разместил операторы SQL, которые были подобраны профилировщиком выше.
Код использует курсор на стороне сервера, для этого и предназначены эти вызовы. Первый набор вызовов - подготовка / открытие курсора. Затем получение строк от курсора. Наконец закрытие курсора. Эти sprocs аналогичны операторам T-SQL OPEN CURSOR, FETCH NEXT, CLOSE CURSOR.
Мне нужно было бы взглянуть поближе (что я и сделаю), но я предполагаю, что что-то происходит с серверным курсором, инкапсулирующей транзакцией и DDL.
Еще несколько вопросов:
Обновлять:
Я не совсем понимаю, что происходит.
Похоже, вы используете курсоры на стороне сервера, поэтому вы можете использовать Recordset.Update () для отправки изменений обратно на сервер в дополнение к выполнению сгенерированных операторов SQL для изменения схемы и обновления данных в динамических таблицах. Использование того же соединения внутри явной транзакции.
Я не уверен, какое влияние операции с курсором окажут на остальную часть транзакции или наоборот, и, честно говоря, я удивлен, что это не работает.
Я не знаю, насколько велико это будет изменение, но я бы рекомендовал отойти от курсоров на стороне сервера и создать операторы UPDATE для обновлений таблиц.
Извини, я ничем не мог больше помочь.
Кстати, я нашел следующую информацию о вызовах sp_cursor:
http://jtds.sourceforge.net/apiCursors.html
Да, я читал в документации, что для выполнения транзакций я не могу использовать курсор на стороне клиента. И я также убедился, что все они используют одно и то же активное соединение. Спасибо, что помогли с этим, это наверняка была странная проблема = (
Хорошая информация о хранимых процедурах ... У меня было ощущение, что он делает что-то подобное, но я не был уверен ... Я никогда не делал Trace до сегодняшнего дня, поэтому я никогда не видел таких процедур, вызываемых под капотом. =) =)
Не беспокойтесь, вы старались изо всех сил, используя предоставленную информацию. Я тоже почесываю голову ... Да, я использую Recordset.Update прямо сейчас для фактических обновлений строк, как вы сказали ... Я попробую использовать вместо этого непосредственно сгенерированный оператор Update и посмотрю, как это работает .
О, он создает команды ADO. Попробую воспользоваться трассировкой и доложу!