Lesson 14. Stored Procedure Techniques Every Developer Should Know

The previous lesson covered the basics of working with stored procedures. To really be effective in working with stored procedures, you must learn additional techniques. In this lesson, you learn about:

Image The SET NOCOUNT statement

Image How to use the @@ functions in the stored procedures that you build

Image How to work with input and output parameters

Image How to add error handling to your stored procedures

The SET NOCOUNT Statement

The SET NOCOUNT statement, when set to ON, eliminates the xx row(s) affected message in the SQL Management Studio window. It also eliminates the DONE_IN_PROC communicated from SQL Server to the client application. For this reason, the SET NOCOUNT ON statement, when included, improves the performance of the stored procedure. Here’s an example:

CREATE PROCEDURE procPersonGetNoCount AS
SET NOCOUNT ON
SELECT BusinessEntityID, FirstName, LastName
FROM Person.Person
ORDER BY LastName, FirstName

If you execute this stored procedure from within SQL Server Management Studio, you’ll notice that the xx row(s) affected message does not appear (see Figure 14.1). You might wonder how with SET NOCOUNT ON in effect, you can return the number of rows affected to the client application. Fortunately, this is easily accomplished with the @@RowCount system variable. The following section covers the @@RowCount system variable as well as other system variables.

Image

FIGURE 14.1 With a NOCOUNT statement, the xx row(s) affected message does not appear.

Using the @@ Functions

Developers often refer to the @@ functions as global variables. In fact, they don’t really behave like variables. You cannot assign values to them or work with them as you would work with normal variables. Instead, they behave as functions that return various types of information about what is going on in SQL Server.

Using the @@RowCount System Variable

The @@RowCount variable returns the number of rows returned by a selected statement or affected by a statement that modifies data. It returns zero if no values are returned by the select statement or modified by the action query. Here’s an example:

CREATE PROCEDURE procPersonGetNoCount AS
SET NOCOUNT ON
SELECT BusinessEntityID, FirstName, LastName
FROM Person.Person
ORDER BY LastName, FirstName
SELECT @@RowCount as NumberOfPeople

The example selects all employees who are sales representatives from the Employee table. It returns the number of rows selected (see Figure 14.2).

Image

FIGURE 14.2 The @@RowCount variable returns the number of rows returned by a selected statement or affected by a statement that modifies data.


Note

You can also see the results of the SELECT @@RowCount statement if you change the query results to display as text rather than as grid. To do this, select Query, Results To, Results to Text.


Using the @@TranCount System Variable

The @@TranCount function is applicable when you are using explicit transactions. Transactions are covered in Lesson 15, “Power Stored Procedure Techniques.” The BEGIN TRAN statement sets the @@TranCount to one. Each ROLLBACK TRAN statement decrements @@TranCount by one. The COMMIT TRAN statement also decrements @@TranCount by one. When you use nested transactions, @@TranCount helps you to keep track of how many transactions are still pending.

Using the @@Identity System Variable

The @@Identity function retrieves the new value inserted into a table that has an identity column. Here’s an example:

INSERT INTO Purchasing.ShipMethod
    (Name, ShipBase, ShipRate)
    VALUES ('Speedy Freight', 120, 150)
    SELECT @@Identity

The example inserts a row into the ShipMethod table. It returns the identity value of the inserted row (see Figure 14.3).

Image

FIGURE 14.3 The @@Identity function retrieves the new value inserted into a table that has an identity column.

Using the @@Error System Variable

The @@Error function returns the number of any error that occurred in the statement immediately preceding it. Here’s an example:

INSERT INTO Sales.SalesOrderDetail
    (CarrierTrackingNumber, OrderQty,
    ProductID, SpecialOfferID,
    UnitPrice, UnitPriceDiscount)
    VALUES (1, 1, 1, 1, 100.00, .05)
    SELECT @@Error

This example attempts to insert a row into the SalesOrderDetail table. If it is successful, @@Error returns zero. If you attempt to insert a sales order detail record without supplying a value for the SalesOrderID field, it returns error number 515, an error indicating that you cannot insert a NULL value into a column (see Figure 14.4).

Image

FIGURE 14.4 The @@Error function returns the number of any error that occurred in the statement immediately preceding it.

It is important to note that @@Error returns the error number associated with the line of code immediately preceding it. Consider this example:

INSERT INTO Sales.SalesOrderDetail
    (CarrierTrackingNumber, OrderQty,
    ProductID, SpecialOfferID,
    UnitPrice, UnitPriceDiscount)
    VALUES (1, 1, 1, 1, 100.00, .05)
    SELECT @@Identity
    SELECT @@Error

Although the INSERT statement renders an error, the @@Error function returns zero (see Figure 14.5) because the SELECT @@Identity statement executes without error.

Image

FIGURE 14.5 An example where the error number is not properly reported.

Working with Parameters

Some stored procedures have no interface. They are called, but they do not receive or return anything. Other procedures are input only. These stored procedures have input parameters but no output parameters. A third type of stored procedure has both input parameters and output parameters.

Input Parameters

An example of a stored procedure that has neither input nor output parameters is this one:

CREATE PROCEDURE procEmployeeGetYoungSalesReps
AS
SELECT BusinessEntityID, JobTitle,
    BirthDate, HireDate,
    VacationHours
FROM HumanResources.Employee
WHERE JobTitle Like 'Marketing %' AND
    BirthDate >= '1/1/1981'
ORDER BY BirthDate

Contrast this stored procedure with the following:

CREATE PROCEDURE procEmployeeGetByTitleAndBirthDate
    @JobTitle nVarChar(50),
    @BirthDate DateTime
AS
SELECT BusinessEntityID, JobTitle,
    BirthDate, HireDate,
    VacationHours
FROM HumanResources.Employee
WHERE JobTitle = @JobTitle AND
    BirthDate >= @BirthDate
ORDER BY BirthDate

The procedure receives two input parameters, @JobTitle and @BirthDate. The procedure uses these input parameters as variables for the WHERE clause for job title and birth date. The easiest way to execute this procedure from within SQL Server Management Studio is the following:

1. Right-click the procedure and select Execute Stored Procedure. The Execute Procedure dialog box appears. Notice in Figure 14.6 that the dialog box prompts you for the two parameters.

Image

FIGURE 14.6 The Execute Procedure dialog box prompts you for all parameters.

2. Enter the parameter values in the Value column and click OK. The results of executing the stored procedure appear.

Another method that you can use to execute the stored procedure is using the New Query toolbar option:

1. Click the New Query toolbar button. A blank query window appears.

2. Type the name of the procedure you want to execute, followed by the parameter values. You must separate each parameter with commas (see Figure 14.7).

Image

FIGURE 14.7 A parameterized stored procedure that returns records if the criteria are met.

3. Click Execute or press F5. The procedure executes.

The parameters in this stored procedure are required. If you do not supply them when you call the procedure, an error results (see Figure 14.8). The following is the same procedure with the job title set up as an optional parameter.

Image

FIGURE 14.8 An error occurs if you omit a required parameter.

CREATE PROCEDURE procEmployeeGetByTitleAndBirthDateOpt
    @JobTitle nVarChar(50) = NULL,
    @BirthDate DateTime
AS
IF @JobTitle IS NULL
    BEGIN
    SELECT BusinessEntityID, JobTitle, BirthDate,
        HireDate, VacationHours
    FROM HumanResources.Employee
    WHERE BirthDate >= @BirthDate
    ORDER BY BirthDate
    END
ELSE
    BEGIN
    SELECT BusinessEntityID, JobTitle, BirthDate,
        HireDate, VacationHours
    FROM HumanResources.Employee
    WHERE JobTitle = @JobTitle AND
        BirthDate >= @BirthDate
    ORDER BY BirthDate
    END

You establish an optional parameter by supplying SQL Server with a default value for the parameter. In this example, @JobTitle is an optional parameter, but @BirthDate is required. If you opt to omit the @JobTitle parameter, you would call the stored procedure like this:

procEmployeesGetByTitleAndBirthDateOpt @BirthDate = '1/1/1960'

Notice that the example supplies the @BirthDate parameter as a named parameter. If you omit the @JobTitle parameter when you call the procedure, the procedure sets its value to Null. The stored procedure evaluates the value of the @JobTitle variable. If it is Null (it wasn’t supplied), the WHERE clause omits the title from the selection criteria. If the user of the stored procedures supplies the @JobTitle parameter, the procedure uses the @JobTitle parameter in the criteria for the WHERE clause.

Output Parameters

So far, we have looked only at input parameters—parameters that the user of the stored procedure supplies to the procedure. You can also declare output parameters in the stored procedures you build. SQL Server returns output parameters to the caller (as their name implies). Here’s an example:

CREATE PROCEDURE procEmployeesGetByTitleAndBirthDateOutput
    @JobTitle nVarChar(50) = NULL,
    @BirthDate DateTime,
    @MyMessage VarChar(50) = NULL OUTPUT
AS
IF @JobTitle IS NULL
    BEGIN
    SELECT BusinessEntityID, JobTitle, BirthDate,
        HireDate, VacationHours
    FROM HumanResources.Employee
    WHERE BirthDate >= @BirthDate
    ORDER BY BirthDate
    SELECT @MyMessage = 'No Title'
    END
ELSE
    BEGIN
    SELECT BusinessEntityID, JobTitle, BirthDate,
        HireDate, VacationHours
    FROM HumanResources.Employee
    WHERE JobTitle = @JobTitle AND
        BirthDate >= @BirthDate
    ORDER BY BirthDate
    SELECT @MyMessage = 'Job Title Supplied'
    END
SELECT @MyMessage

In addition to receiving two parameters, this procedure also has an output parameter called @MyMessage. The IF statement within the procedure sets the value of @MyMessage to the appropriate string (see Figure 14.9). You will see additional examples of OUPUT parameters and their uses as you move through the material. In particular, you will see examples of ADO code that utilize the output parameters within the client/server applications you build.

Image

FIGURE 14.9 The IF statement within the procedure sets the value of @MyMessage to the appropriate string.


Note

To see the output parameter, you can also change the query results to display as text. To do this, select Query, Results To, Results to Text.


Errors and Error Handling

So far, the examples in this lesson have contained no error handling. This means that they leave what happens when an error occurs up to chance. Although T-SQL provides a means of handling errors, the error handling model in T-SQL is not as powerful as that in many programming languages. Because there’s no ON ERROR GOTO statement, you must handle errors as they occur.

Handling Runtime Errors

One alternative to handling errors as they occur is to prevent errors from occurring in the first place. The SalesOrderDetail table in the AdventureWorks database requires that data be entered for the several of the fields. The SalesOrderDetailID field is an IDENTITY column, so this is not of concern. Here’s the error that occurs if a value is not supplied for the SalesOrderID field:

Msg 515, Level 16, State 2, Line 1
Cannot insert the value NULL into column 'SalesOrderID',
table 'AdventureWorks.Sales.SalesOrderDetail';
column does not allow nulls. INSERT fails.
The statement has been terminated.

Here’s an example of how you can prevent this error message from ever occurring:

CREATE PROCEDURE procOrderDetailAddHandleErrors
@SalesOrderID int = Null,
@CarrierTrackingNumber nvarchar(25) = Null,
@OrderQty smallint = 0,
@ProductID int = Null,
@SpecialOfferID int = Null,
@UnitPrice money = 0,
@UnitPriceDiscount money = 0
AS

IF @SalesOrderID Is Null
    BEGIN
        PRINT 'SalesOrderID Cannot be Null'
        RETURN
    END

IF @CarrierTrackingNumber Is Null
    BEGIN
        PRINT 'CarrierTrackingNumber Cannot be Null'
        RETURN
    END

IF @ProductID Is Null
    BEGIN
        PRINT 'ProductID Cannot be Null'
        RETURN
    END

IF @SpecialOfferID Is Null
    BEGIN
        PRINT 'SpecialOfferID Cannot be Null'
        RETURN
    END

INSERT INTO Sales.SalesOrderDetail
(SalesOrderID, CarrierTrackingNumber, OrderQty,
ProductID, UnitPrice, UnitPriceDiscount)
VALUES
(@SalesOrderID, @CarrierTrackingNumber, @OrderQty,
@ProductID, @UnitPrice, @UnitPriceDiscount)

In the example, several of the parameters are optional. The procedure begins by testing to see whether the value of the @SalesOrderID is Null. If it is, a message is printed, and the RETURN statement exits the procedure. This prevents the error message from ever occurring. The code repeats the process of testing the value and printing a message for both the CarrierTrackingNumber and ProductID parameters. Of course, you could add an output parameter that would report the problem back to the client application.

Returning Success and Failure Information from a Stored Procedure

As discussed in the previous section, it is important for the server to communicate to the client application the success or failure information about what happened within the stored procedure. You can select between two techniques to accomplish this task. The first method involves returning a recordset with status information. Here’s an example:

CREATE PROCEDURE procOrderDetailAddHandleErrors2
@SalesOrderID int,
@CarrierTrackingNumber nvarchar(25),
@OrderQty smallint,
@ProductID int,
@UnitPrice money,
@UnitPriceDiscount money
AS
DECLARE @SalesOrderDetailID int,
    @LocalError int,
    @LocalRows int

INSERT INTO Sales.SalesOrderDetail
(SalesOrderID, CarrierTrackingNumber, OrderQty,
ProductID, UnitPrice, UnitPriceDiscount)
VALUES
(@SalesOrderID, @CarrierTrackingNumber, @OrderQty,
@ProductID, @UnitPrice,
@UnitPriceDiscount)

SELECT @SalesOrderDetailID = @@Identity,
    @LocalError = @@Error, @LocalRows = @@RowCount
SELECT @SalesOrderDetailID, @LocalError, @LocalRows

The procedure first declares three variables: @SalesOrderID, @LocalError, and @LocalRows. It then inserts an order into the SalesOrderDetail table. The statement immediately following the INSERT statement populates the three variables with the identity value, error number (if any), and number of rows affected.

In Figure 14.10, you can see that the @SalesOrderID parameter was not passed to the stored procedure. Notice in the results that the identity value of the inserted row was NULL, the error number was 515, and the number of rows affected was 0. This is because an error occurred and the row was never inserted.

Image

FIGURE 14.10 The results of executing a stored procedure that returns error information.

The alternative to this technique is to use output parameters. Here’s an example:

CREATE PROCEDURE procOrderDetailAddHandleErrors3
@SalesOrderID int,
@CarrierTrackingNumber nvarchar(25),
@OrderQty smallint,
@ProductID int,
@UnitPrice money,
@UnitPriceDiscount money,
@SalesOrderDetailID int = 0 OUTPUT,
@LocalError int = 0 OUTPUT,
@LocalRows int = 0 OUTPUT
AS
INSERT INTO Sales.SalesOrderDetail
(SalesOrderID, CarrierTrackingNumber, OrderQty,
ProductID, UnitPrice, UnitPriceDiscount)
VALUES
(@SalesOrderID, @CarrierTrackingNumber, @OrderQty,
@ProductID, @UnitPrice, @UnitPriceDiscount)
SELECT @SalesOrderDetailID = @@Identity,
    @LocalError = @@Error, @LocalRows = @@RowCount
SELECT @SalesOrderDetailID, @LocalError, @LocalRows

Notice that the procedure does not declare any variables. Instead, it contains three output parameters: one for the SalesOrderDetailID, another for the error information, and the last for the number of rows affected. The procedure populates the output parameters just as it populated the variables, with a SELECT statement immediately following the INSERT statement.

Summary

You can use many techniques to make your stored procedures more powerful. In this lesson, you learned many of them. You learned about the SET NOCOUNT statement and how to use the built-in @@ functions. You also learned how to pass parameters to the stored procedures you build as well as how to return values from your stored procedures. Finally, you learned how to add error handling to your stored procedures.

Q&A

Q. Why would you use the SET NOCOUNT statement?

A. When it is set to ON, SET NOCOUNT eliminates the xx row(s) affected message in the SQL Express Manager window and the DONE_IN_PROC communicated from SQL Server to the client application. This improves the performance of the stored procedure.

Q. The @@ functions are actually variables (true/false).

A. False. You cannot assign values to them or work with them like normal variables. They are instead functions that return information about SQL Server.

Q. Describe the parameters used when a stored procedure is used to insert data.

A. You generally have one input parameter for each field in the underlying table. It is also common to have output parameters that return error and status information, as well as the identity value of the row that the stored procedure inserted.

Workshop

Quiz

1. What does @@Identity do?

2. What keyword do you use to return a value from a stored procedure?

3. You use the ON ERROR GOTO statement to write error handling into the stored procedures that you build (true/false).

4. What is the TSQL function you use to determine whether the caller has passed a parameter value to a stored procedure?

Quiz Answers

1. Retrieves the new value inserted into an identity column.

2. The OUTPUT keyword.

3. False. The ON ERROR GOTO statement is not available in T-SQL stored procedures. You must therefore handle errors as they occur.

4. The ISNULL keyword.

Activities

Build a stored procedure that inserts an order into the AdventureWorks Sales.SalesOrderDetail table. Before you attempt to insert the row, write T-SQL to ensure that the SalesOrderID associated with the order detail record is included in the Sales.SalesOrderHeader table. If the sales order associated with the sales order detail is not included in the Sales.SalesOrderHeader table, return an error message to the caller and exit the stored procedure. If the order does exist in the Sales. SalesOrderHeader table, add the order. Return the Identity value of the new row to the caller using an output parameter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.101.192