Question

I currently am working on a legacy application and have inherited some shady SQL with it. The project has never been put into production, but now is on it's way. During intial testing I found a bug. The application calls a stored procedure that calls many other stored procedures, creates cursors, loops through cursors, and many other things. FML.

Currently the way the app is designed, it calls the stored procedure, then reloads the UI with a fresh set of data. Of course, the data we want to display is still being processed on the SQL server side, so the UI results are not complete when displayed. To fix this, I just made a thread sleep for 30 seconds, before loading the UI. This is a terrible hack and I would like to fix this properly on the SQL side of things.

My question is...is it worthwhile to convert the branching stored procedures to functions? Would this make the main-line stored procedure wait for a return value, before processing on?

Here is the stored procedure:

    ALTER PROCEDURE [dbo].[ALLOCATE_BUDGET] 
    @budget_scenario_id uniqueidentifier
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @constraint_type varchar(25)

    -- get project cache id and constraint type
    SELECT @constraint_type = CONSTRAINT_TYPE
    FROM BUDGET_SCENARIO WHERE BUDGET_SCENARIO_ID = @budget_scenario_id

    -- constraint type is Region by Region
    IF (@constraint_type = 'Region by Region')
      EXEC BUDGET_ALLOCATE_SCENARIO_REGIONBYREGION @budget_scenario_id

    -- constraint type is City Wide
    IF (@constraint_type = 'City Wide')
      EXEC BUDGET_ALLOCATE_SCENARIO_CITYWIDE @budget_scenario_id

    -- constraint type is Do Nothing
    IF (@constraint_type = 'Do Nothing')
      EXEC BUDGET_ALLOCATE_SCENARIO_DONOTHING @budget_scenario_id

    -- constraint type is Unconstrained
    IF (@constraint_type = 'Unconstrained')
      EXEC BUDGET_ALLOCATE_SCENARIO_UNCONSTRAINED @budget_scenario_id

    --set budget scenario status to "Allocated", so reporting tabs in the application are populated
    EXEC BUDGET_UPDATE_SCENARIO_STATUS @budget_scenario_id, 'Allocated'
END

To avoid displaying an incomplete resultset in the calling .NET application UI, before the cursors in the branching calls are completed, is it worthwile to convert these stored procedures into functions, with return values? Would this force SQL to wait before completing the main call to the [ALLOCATED_BUDGET] stored procedure?

  • The last SQL statement call in the stored procedure sets a status to "Allocated". This is happening before the cursors in the previous calls are finished processing. Does making these calls into function calls affect how the stored procedure returns focus to the application?

Any feedback is greatly appreciated. I have a feeling I am correct in going towards SQL functions but not 100% sure.

** additional information:

  1. Executing code uses [async=true] in the connection string
  2. Executing code uses the [SqlCommand].[ExecuteNonQuery] method
Was it helpful?

Solution

How are you calling the procedure? I'm going to guess that you are using ExecuteNonQuery() to call the procedure. Try calling the procedure using ExecuteScalar() and modify the procedure like the following:

ALTER PROCEDURE [dbo].[ALLOCATE_BUDGET] 
    @budget_scenario_id uniqueidentifier
AS
BEGIN
   ...

    RETURN True
END

This should cause your data execution code in .NET to wait for the procedure to complete before continuing. If you don't want your UI to "hang" during the procedure execution, use a BackgroundWorkerProcess or something similar to run the query on a separate thread and look for the completed callback to update the UI with the results.

OTHER TIPS

You could also try using the RETURN statement in your child stored procedures, which can be used to return a result code back to the parent procedure. You can call the child procedure by something along the lines of "exec @myresultcode = BUDGET_ALLOCATE_SCENARIO_REGIONBYREGION()". I think this should force the parent procedure to wait for the child procedure to finish.

I have never heard that it's possible for a stored procedure to return to the caller while still executing in the background.

In fact, I'll go as far as to say I don't believe that's happening. If you're seeing a difference between the UI and what you believe the SP should have done, then I believe it has a different cause.

Does the connection string have async=true in it? Is the SP being executed by using BeginExecuteReader or Begin-anything else?

At the risk of sounding to simple, I suggest you could create a table which can store the status of the stored proc. Somehow, a flag that can indicate that the entire process & sub-process has finished executing.

You could query this from UI to see if things are done by polling this status code.

Does making these calls into function calls affect how the stored procedure returns focus to the application?

No.

The stored procedure has no idea that its caller is a UI application. There is nothing in the stored procedure that can influence the behavior of the UI application.

Most likely the UI application is calling the stored procedure on one connection, and then refreshing its data on another connection. There's a plethora of ways of getting the UI to delay refreshing, but the one I'll push is that there should be a single database connection.

Personally, I would be far more concerned about replacing those cursors than converting this to functions.

And I would not run the last proc until checking for a valid return code from the previous procs (this thing is in real trouble if one of the preceding procs dies!)

Also consider if this should all be in a transaction (are these procs changing data in a table?)

(Am I the only one who finds it funny you have a proc to run the process for Do Nothing?)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top