Previous | Table of Contents | Next |
You can set the return status of a procedure with the RETURN statement. Look at this enhanced version of the price lookup procedure we wrote earlier. It sets a non-zero return status if the item requested is not found, or if it doesnt have a non-null price:
drop proc pr_itemprice_lookup go create proc pr_itemprice_lookup (@item_id int, @price money output) as select @price = price from items where item_id = @item_id if @@rowcount = 0 return 75 -- row not found if @price is null return 76 -- price is null return 0 -- normal return - price is ok
Ive used the values 75 and 76 to mean row not found and price is null, respectively. Why 75 and 76? I made them up. I could have used any non-zero number outside of the reserved range (-99 to -1). The important thing is to establish standard definitions for return status and to use those values as widely as possible in your organization.
Well have to rewrite the order insert procedure to capture those values and also to do some error trapping of its own:
drop proc pr_insertorder go create proc pr_insertorder (@item_id int, @qty int, @order_id int output) as declare @priceval money, @retstatus int execute @retstatus = pr_itemprice_lookup @item_id, @priceval output if @retstatus != 0 begin if @retstatus = 75 raiserror (invalid item id: please try again, 16, 1) else if @retstatus = 76 raiserror (there is no price for item id %d, 16, 1, @item_id) else raiserror (General failure in procedure pr_itemprice_lookup, 16, 1) return 99 end insert orders (item_id, qty, unit_price, ext_price) values (@item_id, @qty, @priceval, @qty*@priceval) if @@error != 0 return 77 select @order_id = @@identity return 0
Note: By now, you probably realized that writing stored procs is real programming. Until now, it wasnt all that bad, but now we have real error checking, and the code is getting longer.The fact is, a stored procedure is seldom longer than two or three pages, and almost never longer than ten. If you find yourself writing a ten-page procedure, you probably made a mistake in the design that needs to be corrected. SQL is such a compact language that there is seldom need to write a whole lot of it, even with solid error checking and plenty of commentary.
We should look at some of the new aspects of the procedure:
declare @priceval money, @retstatus int execute @retstatus = pr_itemprice_lookup @item_id, @priceval output
The procedure retrieves a return status after executing the inner procedure, pr_itemprice_lookup.
if @retstatus != 0 begin if @retstatus = 75 raiserror (invalid item id: please try again, 16, 1) else if @retstatus = 76 raiserror (there is no price for item id %d, 16, 1, @item_id) else raiserror (General failure in procedure pr_itemprice_lookup, 16, 1) return 99 end
The error check looks for three conditions: 75, 76, and any other non-zero return status. If you look at pr_itemprice_lookup, the procedure itself returns only with the values 75, 76, and 0. So heres the question: What other non-zero return values could this procedure return? The answer is, any of the SQL Server returns (1 to 99) listed earlier. When you execute a stored procedure from another program, always retrieve and examine the return status, regardless of whether return status is implemented by the procedure programmer.
insert orders (item_id, qty, unit_price, ext_price) values (@item_id, @qty, @priceval, @qty*@priceval) if @@error != 0 return 77
This error check tests the insert to make certain it succeeded. Any non-zero value for @@error should cause the procedure to exit and return a non-zero status.
Notice also that the procedure exits before a value is assigned to @order_id. This is important because the value of @@identity is set the first time (within a SQL Server session) that you insert a row into a table containing an identity column. This value is not automatically nulled or zeroed by a subsequent failed attempt to insert a row. You need to make certain that you have assigned a proper value (often null) to output parameters before exiting in an error case.
Previous | Table of Contents | Next |