2008-09-11 14 views
2

Je suis en train de mettre à jour un système hérité qui permet aux utilisateurs de dicter une partie du schéma de l'une de ses tables. Les utilisateurs peuvent créer et supprimer des colonnes de la table via cette interface. Ce système hérité utilise ADO 2.8 et utilise SQL Server 2005 comme base de données (vous ne voulez même pas savoir quelle base de données il utilisait avant de tenter de moderniser cette bête a commencé ... mais je digresse. =))Mise à jour du schéma et des lignes d'une transaction, SQL Server 2005

Dans ce même processus d'édition, les utilisateurs peuvent définir (et modifier) ​​une liste de valeurs valides pouvant être stockées dans ces champs créés par l'utilisateur (si l'utilisateur souhaite limiter ce qui peut être dans le champ). Lorsque l'utilisateur modifie la liste des entrées valides pour un champ, s'il supprime l'une des valeurs valides, il peut choisir une nouvelle "valeur valide" pour mapper toutes les lignes ayant cette valeur (maintenant non valide) dans il, de sorte qu'ils ont maintenant une valeur valide à nouveau. En regardant l'ancien code, j'ai remarqué qu'il est extrêmement vulnérable de mettre le système dans un état invalide, parce que les changements mentionnés ci-dessus ne sont pas fait dans une transaction (donc si quelqu'un d'autre est venu à mi-chemin du processus mentionné ci-dessus et fait ses propres changements ... eh bien, vous pouvez imaginer les problèmes qui pourraient causer). Le problème est que j'ai essayé de les mettre à jour sous une seule transaction, mais chaque fois que le code arrive à la partie où il change le schéma de cette table, tous les autres changements (mise à jour des valeurs dans les lignes , que ce soit dans la table où le schéma a changé ou non ... ils peuvent même être des tables complètement indépendantes) faites jusqu'à ce point dans la transaction semblent être silencieusement abandonnées. Je ne reçois aucun message d'erreur indiquant qu'ils ont été supprimés, et quand je commets la transaction à la fin aucune erreur n'est soulevée ... mais quand je vais regarder dans les tables qui étaient censées être mises à jour dans la transaction, seules les nouvelles colonnes sont là. Aucune des modifications non-schéma effectuées sont enregistrées.

Regarder sur Internet pour trouver des réponses s'est jusqu'à présent avéré être une perte de quelques heures ... alors je me tourne ici pour obtenir de l'aide. Quelqu'un at-il déjà essayé d'effectuer une transaction via ADO qui met à jour le schéma d'une table et met à jour les lignes dans les tables (que ce soit la même table ou d'autres)? Est-ce que ce n'est pas autorisé? Y a-t-il de la documentation qui pourrait être utile dans cette situation?

EDIT:

D'accord, je ne trace, et ces commandes ont été envoyées à la base de données (explications entre parenthèses)

(Je ne sais pas ce qui se passe ici, on dirait qu'il est la création d'un procédure stockée temporaire ...?)


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 la table qui contient des informations de définition pour les champs générés par les utilisateurs)


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

(Je pense que mon code a été Itère la liste d'entre eux ici, en saisissant la informations actuelles)


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 

(Cela semble être là où je suis entrée dans les données modifiées pour les définitions, je passe par chacun et mettre à jour les changements survenus dans les définitions de la coutume eux-mêmes champs)


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 

(C'est là mon code supprime la suppression par l'interface avant que cette économie a commencé] ... il est aussi la seule chose pour autant que je peux dire que cela arrive effectivement au cours de cette transaction)


ALTER TABLE CustomizableTable DROP COLUMN _weveknown; 

(maintenant, si l'une des définitions ont été modifiés de telle sorte que l'utilisation Les propriétés de la colonne créées par r doivent être modifiées ou les index sur les colonnes doivent être ajoutés/supprimés, cela est fait ici, avec une valeur par défaut pour toutes les lignes qui n'ont pas encore de valeur pour la colonne donnée ... noter que, pour autant que je peux dire, rien de tout cela se fait lorsque la procédure stockée se termine.)

 
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 

(clôture de la transaction ...?)

 
exec sp_cursorclose 180150003 
go 

Après toute cette publicité o ci-dessus, seule la suppression de la colonne se produit. Tout ce qu'il y a avant et après dans la transaction semble être ignoré, et il n'y avait aucun message dans la trace SQL pour indiquer que quelque chose s'est mal passé pendant la transaction.

Répondre

1

Le code utilise un curseur côté serveur, c'est la raison de ces appels. Le premier ensemble d'appels prépare/ouvre le curseur. Puis aller chercher des lignes du curseur. Enfin, fermant le curseur. Ces sprocs sont analogues aux instructions T-SQL OPEN CURSOR, FETCH NEXT et CLOSE CURSOR.

Je devrais regarder de plus près (ce que je vais faire), mais je suppose qu'il se passe quelque chose avec le curseur côté serveur, la transaction d'encapsulation et le DDL.

quelques questions:

  1. Vous sens à utiliser des curseurs côté serveur dans ce cas?
  2. Les commandes ADO utilisent-elles toutes la même connexion active?

Mise à jour:

Je ne suis pas sûr de ce qui se passe. Il semble que vous utilisiez des curseurs côté serveur afin que vous puissiez utiliser Recordset.Update() pour renvoyer les modifications au serveur, en plus d'exécuter des instructions SQL générées pour modifier le schéma et mettre à jour les données dans la table dynamique (s). En utilisant la même connexion, dans une transaction explicite. Je ne suis pas sûr de l'effet que les opérations du curseur auront sur le reste de la transaction, et vice-versa, et pour être honnête, je suis surpris que cela ne fonctionne pas. Je ne sais pas quelle serait l'ampleur d'une modification, mais je recommanderais de quitter les curseurs côté serveur et de créer les instructions UPDATE pour vos mises à jour de table.

Désolé je ne pourrais pas être de plus d'aide.

BTW Je trouve les informations suivantes sur les appels sp_cursor:

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

+0

Oui, je lisais dans la documentation que pour effectuer des transactions, je ne pouvais pas utiliser un Curseur côté client. Merci pour votre aide, ça a été un problème étrange pour sûr = ( – EdgarVerona

+0

Bonne information aussi sur les procédures stockées là ... J'ai eu le sentiment qu'il faisait quelque chose comme ça , mais je n'étais pas sûr ... Je n'avais jamais fait de Trace avant aujourd'hui, donc je n'ai jamais vu ce genre de procédures s'appeler sous le capot. =) =) – EdgarVerona

+0

Pas de soucis, tu as fait de ton mieux pourrait avec l'information donnée. Je me gratte la tête dessus ... Eh bien, j'utilise Recordset.Update maintenant pour les mises à jour de lignes comme vous l'avez dit ... Je vais essayer d'utiliser une instruction Update directement générée et voir comment ça fonctionne . – EdgarVerona

0

Le comportement que vous décrivez est autorisé. Comment le code modifie-t-il le schéma? Construction de SQL à la volée et exécution via une commande ADO? Ou en utilisant ADOX?

Si vous avez accès au serveur de base de données, essayez d'exécuter une trace du profileur SQL tout en testant le scénario que vous avez décrit. Voyez si la trace enregistre des erreurs/annulations.

+0

Oh, il est la construction de commandes ADO. Je vais essayer d'utiliser la trace et de rendre compte! – EdgarVerona

+0

D'accord, j'ai utilisé la trace, mais je n'ai rien vu d'inhabituel. = (J'ai posté les instructions SQL qui ont été récupérées par le Profiler ci-dessus.) – EdgarVerona