[T-SQL] Curseur et procédure stockée récursive

Curseur et procédure stockée récursive [T-SQL] - SQL/NoSQL - Programmation

Marsh Posté le 12-01-2005 à 17:00:23    

Salut,
 
J'ai un léger problème avec SQL Server 2000
 
J'ai fait une procédure stockée, qui est récursive (elle s'appelle elle-même) et elle contient un curseur.
 
En fait, c'est dans la boucle du curseur qu'elle s'appelle elle-même, et il semble que le problème vienne de là.
 
En effet, lorsque je l'éxécute, voici les erreurs que j'obtiens :
 

Code :
  1. Serveur : Msg 16915, Niveau  16, État 1, Procédure objDelete, Ligne 21
  2. Un curseur nommé 'fils' existe déjà.
  3. Serveur : Msg 16915, Niveau  16, État 1, Procédure objDelete, Ligne 21
  4. Un curseur nommé 'fils' existe déjà.
  5. Serveur : Msg 16905, Niveau  16, État 1, Procédure objDelete, Ligne 26
  6. Le curseur est déjà ouvert.
  7. (1 ligne(s) affectée(s))
  8. Serveur : Msg 16916, Niveau  16, État 1, Procédure objDelete, Ligne 35
  9. Un curseur nommé 'fils' n'existe pas.
  10. Serveur : Msg 16916, Niveau  16, État 1, Procédure objDelete, Ligne 38
  11. Un curseur nommé 'fils' n'existe pas.
  12. Serveur : Msg 16916, Niveau  16, État 1, Procédure objDelete, Ligne 39
  13. Un curseur nommé 'fils' n'existe pas.
  14. (1 ligne(s) affectée(s))


 
Voici le code de ma procédure :

Code :
  1. CREATE PROCEDURE dbo.objDelete(@usrlog as varchar(50), @objnum as numeric, @typobj as char(3))
  2. AS
  3. begin
  4.     declare @nb as numeric
  5.     -- On vérifie qu'il n'y a pas de fils encore actifs (interdit)
  6.     select @nb = count(*) from obj where typpar = upper(@typobj) and objpar = @objnum and objsta != 'D'
  7.     if @nb > 0
  8.     begin
  9.         RAISERROR ('Impossible de supprimer un noeud ayant encore des fils non supprimés', 16, 1)
  10.     end
  11.     -- Maintenant, on regarde le status. Si le noeud est déjà flagé comme supprimé, on le supprime pour de bon ainsi que tous ses fils, sinon on le marque comme supprimé
  12.     declare @objsta as char(1)
  13.     select @objsta = objsta from obj where typobj = upper(@typobj) and objnum = @objnum
  14.     if @objsta != 'D'
  15.     begin
  16.         -- On flag le noeud actif en supprimé
  17.         update obj set objsta = 'D', usrmod = upper(@usrlog), dtemod = getDate() where typobj = upper(@typobj) and objnum = @objnum
  18.     end
  19.     else
  20.     begin
  21.         -- On va supprimer tous les fils déjà supprimés (grace au check initial, si on est ici, c'est que les eventuels fils sont déjà forcément flagé comme supprimés)
  22.         declare @filsnum as numeric
  23.         declare @filstyp as char(3)
  24.         DECLARE fils CURSOR FOR
  25.         SELECT objnum, typobj
  26.         FROM obj
  27.         WHERE objsta = 'D'
  28.         AND typpar = upper(@typobj) and objpar = @objnum
  29.         OPEN fils
  30.         FETCH NEXT FROM fils
  31.         INTO @filsnum, @filstyp
  32.         WHILE @@FETCH_STATUS = 0
  33.         BEGIN
  34.             -- On supprime chaque fils (appel récursif, afin de contrôler les petits-fils)
  35.             execute objDelete @usrlog, @filsnum, @filstyp
  36.             FETCH NEXT FROM fils
  37.             INTO @filsnum, @filstyp
  38.         END
  39.         CLOSE fils
  40.         DEALLOCATE fils
  41.         -- On supprime définitivement le noeud flagé comme supprimé
  42.         delete obj where  typobj = upper(@typobj) and objnum = @objnum and objsta = 'D'
  43.     end
  44. end
  45. GO


 
Contenu de la base au moment de l'erreur (les fils de l'article 1 sont au status "D", et lui-même aussi. :

Code :
  1. objnum               typobj objsta objnam                                             objpar               typpar
  2. -------------------- ------ ------ -------------------------------------------------- -------------------- ------
  3. 1                    ART    D      Article 1                                          NULL                 NULL
  4. 1                    REP    D      Chapitre 1                                         1                    ART
  5. 2                    ART    C      Article 2                                          NULL                 NULL
  6. 2                    REP    D      Chapitre 2                                         1                    ART
  7. 3                    REP    C      Chapitre 1                                         2                    ART
  8. 4                    REP    C      Chapitre 2                                         2                    ART


 
Commande exécutée :

Code :
  1. execute objDelete 'SDEVIDAL', 1, 'ART'


 
Les autres étapes se sont bien déroulées :

Code :
  1. execute objDelete 'SDEVIDAL', 1, 'ART'
  2. -> Retourne une erreur "'Impossible de supprimer un noeud ayant encore des fils non supprimés"
  3. execute objDelete 'SDEVIDAL', 1, 'REP'
  4. -> OK (réponse 1 flagée comme supprimée)
  5. execute objDelete 'SDEVIDAL', 2, 'REP'
  6. -> OK (réponse 2 flagée comme supprimée)
  7. execute objDelete 'SDEVIDAL', 1, 'ART'
  8. -> OK (article 1 flagé comme supprimé)


 
Mais c'est la dernière étape qui foire, c'est à dire le seul moment où je rentre dans mon curseur et que je tente l'appelle récursif... :/
 
D'après les conventions de nommage de SQL Server, mettre un "@" permet d'expliciter que j'utilise une variable locale. J'ai donc tenté d'en mettre un au nom de mon curseur, mais à ce moment j'obtiens une erreur à la compilation sur le "FOR" du "declare cursor @fils for select ..." donc j'en déduit que cette règle de nommage ne s'applique pas aux curseurs.

Reply

Marsh Posté le 12-01-2005 à 17:00:23   

Reply

Marsh Posté le 12-01-2005 à 17:28:24    

Argh !!!
 

Code :
  1. Syntaxe SQL-92
  2. DECLARE cursor_name [ INSENSITIVE ] [ SCROLL ] CURSOR
  3. FOR select_statement
  4. [ FOR { READ ONLY | UPDATE [ OF column_name [ ,...n ] ] } ]
  5. Syntaxe étendue Transact-SQL
  6. DECLARE cursor_name CURSOR
  7. [ LOCAL | GLOBAL ]
  8. [ FORWARD_ONLY | SCROLL ]
  9. [ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
  10. [ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
  11. [ TYPE_WARNING ]
  12. FOR select_statement
  13. [ FOR UPDATE [ OF column_name [ ,...n ] ] ]


 

Citation :


LOCAL
 
Spécifie que le curseur est de portée locale pour le lot d'instructions, la procédure stockée ou le déclencheur dans lequel il a été créé. Le nom du curseur n'est valide que dans cette portée. Le curseur peut être référencé par des variables de curseur locales du lot d'instructions, de la procédure stockée ou du déclencheur, ou bien par un paramètre OUTPUT d'une procédure stockée. Un paramètre OUTPUT est utilisé pour transmettre le curseur local au lot d'instructions, à la procédure stockée ou au déclencheur effectuant l'appel qui peut affecter le paramètre à une variable de curseur pour référencer le curseur à la fin de la procédure stockée. Le curseur est désalloué implicitement à la fin du lot d'instructions, de la procédure stockée ou du déclencheur à moins d'avoir été renvoyé dans un paramètre OUTPUT. S'il a été renvoyé dans un paramètre OUTPUT, le curseur est désalloué lorsque la dernière variable qui y fait référence est désallouée, ou lorsqu'il est hors de portée.


 
:o

Code :
  1. DECLARE fils CURSOR LOCAL FOR
  2.         SELECT objnum, typobj
  3.         FROM obj
  4.         WHERE typpar = upper(@typobj) and objpar = @objnum


 
Et ça marche [:spamafote]
 
 
Pfffff... Ca m'énerve, elle est trop bien foutue cette doc de SQL Server : même les trucs à la con, si on prend le temps de tout lire on trouve une réponse au problème... :bounce:


Message édité par Arjuna le 12-01-2005 à 17:28:47
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed