views:

51

answers:

0

I have a problem with a recursive SQL function. The original problem is that I've got a list of employees, each of them has various trainings. Each of these trainings has some pre requirements. For example to have your Class 1 drivers license you must have your Class 5. If I remove the Class 5 I need to check disable the Class 1.

Now as this is a tree with no fixed max depth (actually I stop checking at 10ish) I decided to use recursion. I wrote two stored procedures

[dbo].[spCheckTrainingPreqs]
@PIN int,
@training_id int,
@missingTraining int OUTPUT

and

[dbo].[spCheckTrainingPreqsEmployee] 
@PIN int

Now when I call spCheckTrainingPreqsEmployee with an employee number it creates a cursor for each pin/training_id combo and calls spCheckTrainingPreqs. spCheckTrainingPreqs uses a recursive local cursor to walk the tree.

Now the kicker. It works fine in visual studio

DECLARE @return_value int

EXEC    @return_value = [dbo].[spCheckTrainingPreqsEmployee]
        @PIN = 12673

SELECT  'Return Value' = @return_value

GO

But if I go into visual studio and add it to a table adapter as a stored proc with no return I get the error "Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32)"

I've checked that none of my tables have triggers. The only complexity is that many of the tables have views based off of them with sub queries and even a c# coded custom concatenation function. Remember running it from SQL manager works fine.

Full code for reference

/****** Object:  StoredProcedure [dbo].[spCheckTrainingPreqs]    Script Date: 08/31/2010 10:13:36 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[spCheckTrainingPreqs]
@PIN int,
@training_id int,
@missingTraining int OUTPUT
AS
-- get the Prerequisites
declare @requiresID int
declare CurPrereqs cursor local for SELECT RequiresID FROM TrainingPrerequisites WHERE SourceID = @training_id
SET @missingTraining = 0
OPEN CurPrereqs
FETCH NEXT FROM CurPrereqs INTO @requiresID 
WHILE @@FETCH_STATUS = 0
BEGIN
    IF (@missingTraining = 0) -- stop when a missing training is found
    BEGIN
        IF (SELECT count(training_id) FROM employee_training WHERE PIN = @PIN AND training_id = @requiresID GROUP BY training_id) = 1
        BEGIN
            --they have the training 
            IF (@@NESTLEVEL  < 10) -- we only check 10 levels deep
            BEGIN
                EXEC spCheckTrainingPreqs @PIN, @requiresID, @missingTraining
                UPDATE employee_training SET missingPreReq = @missingTraining WHERE training_id = @training_id and PIN = @PIN;
            END
        END
        ELSE
        BEGIN
            SET @missingTraining = @requiresID
            UPDATE employee_training SET missingPreReq = @missingTraining WHERE training_id = @training_id and PIN = @PIN;
            CLOSE CurPrereqs
            DEALLOCATE CurPrereqs;
            RETURN 
        END

    END
FETCH NEXT FROM CurPrereqs INTO @requiresID 
END
CLOSE CurPrereqs
DEALLOCATE CurPrereqs;

and

/****** Object:  StoredProcedure [dbo].[spCheckTrainingPreqsEmployee]    Script Date: 08/31/2010 10:28:27 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Create date: 08/31/2010
-- Description: Checks all pre reqs for an employee
-- =============================================
ALTER PROCEDURE [dbo].[spCheckTrainingPreqsEmployee] 
    @PIN int
AS
BEGIN
    SET NOCOUNT ON;
    declare @training_id int
    declare @missingTraining int
    SET @missingTraining = 0
    declare CurPrereqsE cursor local for SELECT training_id FROM employee_training WHERE PIN = @PIN
    OPEN CurPrereqsE
    FETCH NEXT FROM CurPrereqsE INTO @training_id 
    WHILE @@FETCH_STATUS = 0
    BEGIN
        EXEC spCheckTrainingPreqs @PIN, @training_id, @missingTraining
    FETCH NEXT FROM CurPrereqsE INTO @training_id 
    END
    CLOSE CurPrereqsE
    DEALLOCATE CurPrereqsE;
END