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