Question

Je mets actuellement à jour un système existant qui permet aux utilisateurs de dicter une partie du schéma d'une de ses tables.Les utilisateurs peuvent créer et supprimer des colonnes du tableau via cette interface.Cet ancien système 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 le début de la tentative de modernisation de cette bête...mais je m'éloigne du sujet.=) )

Dans ce même processus d'édition, les utilisateurs peuvent définir (et modifier) ​​une liste de valeurs valides qui peuvent être stockées dans ces champs créés par l'utilisateur (si l'utilisateur souhaite limiter ce qui peut contenir le champ).

Lorsque l'utilisateur modifie la liste des entrées valides pour un champ, s'il supprime l'une des valeurs valides, il est autorisé à choisir une nouvelle "valeur valide" pour mapper toutes les lignes contenant cette valeur (désormais invalide), de sorte que ils ont maintenant à nouveau une valeur valide.

En parcourant l'ancien code, j'ai remarqué qu'il est extrêmement vulnérable à mettre le système dans un état invalide, car les modifications mentionnées ci-dessus ne sont pas effectuées dans le cadre d'une transaction (donc si quelqu'un d'autre arrivait à mi-chemin du processus mentionné ci-dessus et effectuait son propres changements...eh bien, vous pouvez imaginer les problèmes que cela pourrait causer).

Le problème est que j'ai essayé de les mettre à jour en une seule transaction, mais chaque fois que le code arrive à la partie où il modifie 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...il peut même s'agir de tables totalement indépendantes) créées jusqu'à ce stade de la transaction semblent avoir été supprimées silencieusement.Je ne reçois aucun message d'erreur indiquant qu'ils ont été abandonnés, et lorsque je valide la transaction à la fin, aucune erreur n'est généré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 apportées non liées au schéma n'est enregistrée.

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

MODIFIER:

Bon, j'ai fait un 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 que cela crée une 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

(Récupération de la table contenant les informations de définition pour les champs générés par l'utilisateur)


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 parcourait la liste ici, récupérant les 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'endroit où je saisis les données modifiées pour les définitions, je les passe en revue et mets à jour toutes les modifications survenues dans les définitions des champs personnalisés eux-mêmes)


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 ici que mon code supprime les éléments supprimés via l'interface avant le début de cette sauvegarde]...c'est aussi la SEULE chose que je sache qui se produit réellement lors de cette transaction)


ALTER TABLE CustomizableTable DROP COLUMN _weveknown;

(Maintenant, si l'une des définitions a été modifiée de telle manière que les propriétés de la colonne créée par l'utilisateur doivent être modifiées ou que les index sur les colonnes doivent être ajoutés/supprimés, cela se fait ici, en donnant une valeur par défaut à toutes les lignes. qui n'avait pas encore de valeur pour la colonne donnée...notez que, pour autant que je sache, RIEN de tout cela ne se produit réellement 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 tout ce qui précède, seule la suppression de la colonne se produit.Tout ce qui se passe avant et après dans la transaction semble être ignoré et il n'y a aucun message dans SQL Trace pour indiquer que quelque chose s'est mal passé pendant la transaction.

Était-ce utile?

La solution

Le code utilise un curseur côté serveur, c'est à cela que servent ces appels.La première série d’appels prépare/ouvre le curseur.Puis récupérer les lignes du curseur.Enfin, fermez le curseur.Ces sprocs sont analogues aux instructions OPEN CURSOR, FETCH NEXT, CLOSE CURSOR T-SQL.

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

Encore quelques questions :

  1. Voulez-vous 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 exactement sûr de ce qui se passe.

Il semble que vous utilisez 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 ou les tables dynamiques.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, ou vice versa, et pour être honnête, je suis surpris que cela ne fonctionne pas.

Je ne sais pas quelle serait l'ampleur du changement, mais je recommanderais de s'éloigner des curseurs côté serveur et de créer les instructions UPDATE pour les mises à jour de vos tables.

Désolé, je ne pourrais pas être plus utile.

BTW- J'ai trouvé les informations suivantes sur les appels sp_cursor :

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

Autres conseils

Le comportement que vous décrivez est autorisé.Comment le code effectue-t-il les modifications du schéma ?Construire SQL à la volée et l'exécuter via une commande ADO ?Ou utiliser ADOX ?

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top