12
votes

Une procédure stockée de manière récursive est-elle possible dans SQL Server?

Voici ce que j'ai en tant que sous-programme VBScript:

CREATE PROCEDURE usp_build_child_admin_string_hierarchically
    @ID AS INT,
    @ADMIN_STRING AS VARCHAR(8000),
    @ID_STRING AS VARCHAR(8000) OUTPUT
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @index int;
    DECLARE @length int;
    DECLARE @admin_id int;
    DECLARE @new_string varchar(8000);

    SET @index = 1;
    SET @length = 0;
    SET @new_string = @ADMIN_STRING;

    CREATE TABLE #Temp (ID int)

    WHILE @index <= LEN(@new_string)
    BEGIN
        IF CHARINDEX(',', @new_string, @index) = 0
            SELECT @length = (LEN(@new_string) + 1) - @index;
        ELSE
            SELECT @length = (CHARINDEX(',', @new_string, @index) - @index);
        SELECT @admin_id = CONVERT(INT,SUBSTRING(@new_string, @index, @length));
        SET @index = @index + @length + 1;
        INSERT INTO #temp VALUES(@admin_id);
    END

    DECLARE TableCursor CURSOR FOR
        SELECT Admin_ID FROM Administrator_Owners WHERE Admin_ID NOT IN (SELECT ID FROM #temp) AND Owner_ID = @ID;

    OPEN TableCursor;
    FETCH NEXT FROM TableCursor INTO @admin_id;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        IF LEN(@ID_STRING) > 0
        SET @ID_STRING = @ID_STRING + ',' + CONVERT(VARCHAR, @admin_id);
        ELSE
        SET @ID_STRING = CONVERT(VARCHAR, @admin_id);

        EXEC usp_build_child_admin_string_hierarchically @admin_id, @ID_STRING, @ID_STRING;

        FETCH NEXT FROM TableCursor INTO @admin_id;
    END

    CLOSE TableCursor;
    DEALLOCATE TableCursor;

    DROP TABLE #temp;
END
GO


2 commentaires

Je suppose que votre erreur se produit car l'appel récursif est fabriqué avant que le curseur TableCursor est fermé. Serait-il possible de donner au curseur un nom dynamique (peut-être Nappersorn , où n est la profondeur de la récursivité - vous devez faire cela comme paramètre supplémentaire)?


Le problème n'est pas une récursion, qui est certainement autorisée ( MSDN .microsoft.com / fr-US / Bibliothèque / AA175801 (SQL.80) .aspx ), c'est que vous utilisez un nom de curseur statique. Je n'en sais pas assez sur les curseurs des serveurs MS SQL pour poster cela comme une réponse, car il serait avoir de dire comment utiliser un curseur dans cette situation pour être utile! :-)


3 Réponses :


2
votes

Vous pouvez, mais ce n'est généralement pas une bonne idée. SQL est fabriqué pour des opérations basées sur l'ensemble. De plus, dans MS SQL Server au moins, la récursivité est limitée au nombre d'appels récursifs qu'il peut apporter. Vous ne pouvez nid jusqu'à 32 niveaux de profondeur.

Le problème dans votre cas est que le curseur dure à travers chaque appel, vous finissez donc de le créer plus d'une fois.


0 commentaires

10
votes

Le problème est que lorsque votre curseur n'est pas global, il est un curseur de session. Puisque vous faites de la récursivité, même si chaque itération crée un curseur dans une nouvelle pérose Proc, ils sont tous créés dans le même PID (connexion) en même temps, donc la collision.

Vous devrez générer des noms de curseurs uniques dans chaque itération de la procédure en fonction de certains critères qui ne seront pas reproduits pendant la récursion.

ou, de préférence, trouver un moyen de faire ce dont vous avez besoin en utilisant la logique définie et de gérer toute récursion nécessaire à l'aide d'un CTE récursif.


1 commentaires

Quelle est la meilleure façon de générer des noms de curseurs uniques? Je ne suis pas trop bon avec Dynamic SQL.



44
votes

Vous pouvez spécifier un local curseur, comme ceci: xxx

au moins dans SQL Server 2008 R2 (ma machine), cela vous permet d'appeler de manière récursive la SPRO sans fonctionner dans "le curseur existe déjà".


1 commentaires

Je peux également confirmer ces œuvres pour 2005. MSDN.MicRosoft.com/en-us/Library /ms180169(V=SQL.90).aspx Cette réponse doit être marquée comme correcte vraiment.