2008-09-11 6 views
2

Ich aktualisiere derzeit ein Legacy-System, mit dem Benutzer einen Teil des Schemas einer seiner Tabellen diktieren können. Benutzer können über diese Schnittstelle Spalten aus der Tabelle erstellen und entfernen. Dieses Legacy-System verwendet ADO 2.8 und verwendet SQL Server 2005 als Datenbank (Sie wollen nicht einmal wissen, welche Datenbank es verwendet hat, bevor der Versuch, dieses Biest zu modernisieren, begonnen hat ... aber ich schweife ab.)Update Schema und Zeilen in einer Transaktion, SQL Server 2005

Im selben Bearbeitungsprozess können Benutzer eine Liste gültiger Werte definieren (und ändern), die in diesen benutzererstellten Feldern gespeichert werden können (wenn der Benutzer einschränken möchte, was sich in dem Feld befinden kann).

Wenn der Benutzer die Liste der gültigen Einträge für ein Feld ändert, wenn er einen gültigen Wert entfernt, kann er einen neuen "gültigen Wert" auswählen, um alle Zeilen mit diesem (jetzt ungültigen) Wert zuzuordnen es, damit sie jetzt wieder einen gültigen Wert haben. Beim Durchsehen des alten Codes habe ich gemerkt, dass es extrem anfällig dafür ist, das System in einen ungültigen Zustand zu versetzen, weil die oben erwähnten Änderungen nicht innerhalb einer Transaktion ausgeführt werden (wenn also jemand anders auf halbem Weg durch den erwähnten Prozess kam) oben und machte ihre eigenen Änderungen ... nun, Sie können sich vorstellen, die Probleme, die verursachen könnten).

Das Problem ist, ich habe versucht, sie unter einer einzigen Transaktion zu aktualisieren, aber immer wenn der Code zu dem Teil kommt, wo es das Schema dieser Tabelle ändert, alle anderen Änderungen (Aktualisieren von Werten in Zeilen , sei es in der Tabelle, wo sich das Schema geändert hat oder nicht ... sie können völlig unabhängige Tabellen sein, die bis zu diesem Punkt in der Transaktion gemacht wurden, scheinen stillschweigend fallengelassen zu werden. Ich erhalte keine Fehlermeldung, die besagt, dass sie gelöscht wurden, und wenn ich die Transaktion am Ende festschreibe, wird kein Fehler ausgelöst ... aber wenn ich in die Tabellen schaue, die in der Transaktion aktualisiert werden sollen, nur die neuen Spalten gibt es. Keine der vorgenommenen Nicht-Schema-Änderungen wird gespeichert.

Im Netz nach Antworten zu suchen hat sich bis jetzt als eine Verschwendung von ein paar Stunden erwiesen ... also wende ich mich hier um Hilfe. Hat jemand jemals versucht, eine Transaktion über ADO durchzuführen, die sowohl das Schema einer Tabelle aktualisiert als auch Zeilen in Tabellen aktualisiert (sei es dieselbe Tabelle oder andere)? Ist es nicht erlaubt? Gibt es Unterlagen, die in dieser Situation hilfreich sein könnten?

EDIT:

Okay, ich habe eine Spur, und diese Befehle wurden in die Datenbank (Erläuterungen in Klammern) gesendet

(ich weiß nicht, was hier geschieht, sieht aus wie es ist ein zu schaffen Temporär gespeicherte Prozedur ...?)


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

(retreiving die Tabelle, die hier hält, Definitionsinformationen für die Benutzer-generierte Felder)


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

(Ich glaube, mein Code wurde Iterieren durch die Liste von ihnen greifen die aktuelle Informationen)


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 

(Dies scheint zu sein, wo ich die geänderten Daten für die Definitionen bin Eingabe, gehe ich durch jeden und alle Änderungen aktualisieren, die in den Definitionen aufgetreten für die Sitte selbst Felder)


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 

(Dies ist, wo meine Code entfernt die durch die Schnittstelle gelöscht, bevor diese Einsparung] begann ... es ist auch das einzige, was, soweit ich das passiert eigentlich während dieser Transaktion sagen kann)


ALTER TABLE CustomizableTable DROP COLUMN _weveknown; 

(Nun, wenn eine der Definitionen wurden so verändert, dass die Verwendung Die Eigenschaften von r-created-Spalten müssen geändert werden oder Indizes für die Spalten müssen hinzugefügt/entfernt werden. Dies wird hier gemacht, zusammen mit einem Standardwert für alle Zeilen, die noch keinen Wert für die angegebene Spalte haben ... beachten Sie, dass, soweit ich das beurteilen kann, nichts von alldem tatsächlich passiert, wenn die gespeicherte Prozedur beendet ist.)

 
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 

(Abschluss der Transaktion ...?)

 
exec sp_cursorclose 180150003 
go 

Nach all dieser Anzeige o oben erfolgt nur das Löschen der Spalte. Alles davor und danach in der Transaktion scheint ignoriert zu werden, und es gab keine Nachrichten in der SQL-Ablaufverfolgung, um anzuzeigen, dass während der Transaktion etwas schief gelaufen ist.

Antwort

1

Der Code verwendet einen serverseitigen Cursor, für den diese Aufrufe bestimmt sind. Der erste Satz von Anrufen bereitet den Cursor vor/öffnet ihn. Dann Zeilen vom Cursor holen. Schließlich den Cursor schließen. Diese Sprocs sind analog zu den T-SQL-Anweisungen OPEN CURSOR, FETCH NEXT und CLOSE CURSOR.

Ich müsste genauer hinschauen (was ich tun werde), aber ich vermute, dass etwas mit dem serverseitigen Cursor, der Kapselungstransaktion und der DDL passiert.

einige weitere Fragen:

  1. Sind das heißt, Sie serverseitige Cursors in diesem Fall zu benutzen?
  2. Verwenden die ADO-Befehle alle die gleiche aktive Verbindung?

Update:

Ich bin nicht ganz sicher, was los ist.

Es sieht so aus, als ob Sie serverseitige Cursors verwenden, sodass Sie Recordset.Update() verwenden können, um Änderungen zurück auf den Server zu übertragen und generierte SQL-Anweisungen auszuführen, um das Schema zu ändern und Daten in der dynamischen Tabelle zu aktualisieren (s). Verwenden derselben Verbindung innerhalb einer expliziten Transaktion.

Ich bin nicht sicher, welche Auswirkungen die Cursor-Operationen auf den Rest der Transaktion haben werden, und umgekehrt, und um ehrlich zu sein, ich bin überrascht, dass dies nicht funktioniert.

Ich weiß nicht, wie groß eine Änderung wäre, aber ich würde empfehlen, weg von den serverseitigen Cursors zu gehen und die UPDATE-Anweisungen für Ihre Tabellenaktualisierungen zu erstellen.

Sorry, ich könnte nicht mehr helfen.

BTW fand ich die folgenden Informationen über die sp_cursor Anrufe:

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

+0

Aye, ich habe in der Dokumentation gelesen, dass ich keinen clientseitigen Cursor verwenden kann, um Transaktionen durchzuführen, und ich habe auch sichergestellt, dass sie das sind alle mit der gleichen aktiven Verbindung auch. Vielen Dank für die Hilfe mit diesem, es war ein seltsames Problem sicher = ( – EdgarVerona

+0

) Gute Infos zu den gespeicherten Prozeduren dort ... Ich hatte das Gefühl, dass es so etwas tat , aber ich war mir nicht sicher ... Ich hatte noch nie einen Trace vor heute gemacht, also habe ich nie gesehen, dass solche Prozeduren unter der Haube aufgerufen werden. =) =) – EdgarVerona

+0

Keine Sorge, du hast das Beste ausprobiert Könnte mit den Informationen angegeben werden. Ich kratze meinen Kopf darauf gut ... Aye, ich benutze Recordset.Update jetzt für die tatsächlichen Zeile Updates, wie Sie sagten ... Ich werde versuchen, mit einer direkt generierten Update-Anweisung statt und sehen, wie das funktioniert . – EdgarVerona

0

Das von Ihnen beschriebene Verhalten ist zulässig. Wie ändert sich der Code, der das Schema ändert? SQL im laufenden Betrieb erstellen und über einen ADO-Befehl ausführen? Oder mit ADOX?

Wenn Sie Zugriff auf den Datenbankserver haben, versuchen Sie, eine SQL Profiler-Ablaufverfolgung auszuführen, während Sie das umrissene Szenario testen. Überprüfen Sie, ob die Ablaufverfolgung Fehler/Rollbacks protokolliert.

+0

Oh, es ADO Befehle baut. Ich werde versuchen, die Spur zu verwenden und Bericht zu erstatten! – EdgarVerona

+0

Okay, habe die Spur benutzt, aber ich habe nichts Ungewöhnliches gesehen. = (Ich habe die SQL-Anweisungen gepostet, die vom Profiler oben abgeholt wurden.) – EdgarVerona