You can, however, execute dynamic SQL that creates the object: CREATE PROC create_other_proc AS EXEC ‘CREATE PROC get_au_lname AS SELECT au_lname from authors RETURN’ TIP If you are usin
Trang 1Check the @@errorsystem function after each SQL statement, especially insert,
update, and delete, to verify that the statements executed successfully Return a
status code other than 0if a failure occurs
Be sure to comment your code so that when you or others have to maintain it, the
code is self-documenting
Consider using a source code management system, such as Microsoft Visual Studio
SourceSafe, CVS, or Subversion, to maintain versions of your stored procedure
source code
You should avoid using select *in your stored procedure queries If someone were to
add columns to or remove columns from a table, the stored procedure would generate a
different result set, which could cause errors with the applications
Whenever using INSERTstatements in stored procedures, you should always provide the
column list associated with the values being inserted This allows the procedure to
continue to work if the table is ever rebuilt with a different column order or additional
columns are added to the table Listing 44.1 demonstrates what happens if the column list
is not provided and a column is added to the referenced table
LISTING 44.1 Lack of Column List in INSERT Statement Causing Procedure to Fail
use bigpubs2008
go
IF EXISTS ( SELECT * FROM sys.procedures
WHERE schema_id = schema_id(‘dbo’) AND name = N’insert_publishers’) DROP PROCEDURE dbo.insert_publishers
GO
create proc insert_publishers @pub_id char(4),
@pub_name varchar(40),
@city varchar(20),
@state char(2),
@country varchar(30) as
INSERT INTO bigpubs2008.dbo.publishers
VALUES(@pub_id, @pub_name, @city, @state, @country)
if @@error = 0
print ‘New Publisher added’
go
exec insert_publishers ‘9950’, ‘Sams Publishing’, ‘Indianapolis’, ‘IN’, ‘USA’
go
New Publisher added
Trang 2alter table publishers add street varchar(80) null
go
exec insert_publishers ‘9951’, ‘Pearson Education’, ‘Indianapolis’, ‘IN’, ‘USA’
go
Msg 213, Level 16, State 1, Procedure insert_publishers, Line 7
Insert Error: Column name or number of supplied values does not match table
definition.
A stored procedure cannot directly create schemas, views, triggers, defaults, rules,
aggre-gates, functions, or stored procedures You can, however, execute dynamic SQL that
creates the object:
CREATE PROC create_other_proc AS
EXEC (‘CREATE PROC get_au_lname AS
SELECT au_lname from authors
RETURN’)
TIP
If you are using dynamic SQL to create objects in stored procedures, be sure to qualify
each object with the name of the object schema if users other than the stored
proce-dure owner will be executing the stored proceproce-dure
You can create tables in stored procedures Generally, only temporary tables are created in
stored procedures Temporary tables created in stored procedures are dropped
automati-cally when the procedure terminates Global temporary tables, however, exist until the
connection that created them terminates
If you don’t qualify object names within a stored procedure, they default to the schema of
the stored procedure It is recommended that objects in stored procedures be qualified
with the appropriate schema name to avoid confusion
You cannot drop a table and re-create another table with the same name within the
proce-dure unless you use dynamic SQL to execute a string that creates the table
A stored procedure cannot issue the USEstatement to change the database context in
which it is running; the database context for execution is limited to a single database If
you need to reference an object in another database, you should qualify the object name
with the database name in your procedure code
Calling Stored Procedures from Transactions
Stored procedures can be called from within a transaction, and they can also initiate
transactions SQL Server notes the transaction nesting level, which is available from the
@@trancountfunction, before calling a stored procedure If the value of@@trancount
Trang 3when the procedure returns is different from the value of@@trancountwhen it was
executed, SQL Server displays error message 266:Transaction count after EXECUTE
indicates that a COMMIT or ROLLBACK TRAN is missing This message indicates that
transaction nesting is out of balance Because a stored procedure does not abort the batch
on arollback transactionstatement, arollback transactionstatement inside the
procedure could result in a loss of data integrity if subsequent statements are executed
and committed
Arollback transactionstatement rolls back all statements to the outermost transaction,
including any work performed inside nested stored procedures that have not been fully
committed A commit tranwithin the stored procedure decreases the value of
@@trancountby only one Because the transaction is not fully committed until
@@trancountreturns to zero, the work can be completely rolled back at any time prior to
that Essentially, the nested transaction inside the stored procedure is largely ignored The
modifications within the procedure are committed or rolled back based on the final action
taken for the outermost transaction
To avoid transaction nesting issues, you need to develop a consistent error-handling
strat-egy for failed transactions or other errors that occur in transactions within your stored
procedures and implement that strategy consistently across all procedures and
applica-tions Within stored procedures that might be nested, you need to check whether the
procedure is already being called from within a transaction before issuing anotherbegin
transtatement If a transaction is already active, you can issue asave transtatement so
that the procedure can roll back only the work that it has performed and allow the calling
procedure that initiated the transaction to determine whether to continue or abort the
overall transaction
To maintain transaction integrity when calling procedures that involve transactions,
follow these guidelines:
Make no net change to @@trancountwithin your stored procedures
Issue a begin tranonly if no transaction is already active
Set a savepoint if a transaction is already active so that a partial rollback can be
performed within the stored procedure
Implement appropriate error handling and return an error status code if something
goes wrong and a rollback occurs
Issue a commit tranonly if the stored procedure issued the begin transtatement
Listing 44.2 provides a template for a stored procedure that can ensure transactional
integrity whether it is run as part of an ongoing transaction or independently
LISTING 44.2 Template Code for a Stored Procedure That Can Run as Part of a Transaction or
Run as Its Own Transaction
/* proc to demonstrate no net change to @@trancount
** but rolls back changes within the proc
** VERY IMPORTANT: return an error code
Trang 4** to tell the calling procedure rollback occurred */
create proc ptran1
as
declare @trncnt int
select @trncnt = @@trancount — save @@trancount value
if @trncnt = 0 — transaction has not begun
begin tran ptran1 — begin tran increments nest level to 1
else — already in a transaction
save tran ptran1 — save tran doesn’t increment nest level
/* do some processing */
if (@@error != 0) — check for error condition
begin
rollback tran ptran1 — rollback to savepoint, or begin tran
return 25 — return error code indicating rollback
end
/* more processing if required */
if @trncnt = 0 — this proc issued begin tran
commit tran ptran1 — commit tran, decrement @@trancount to 0
— commit not required with save tran return 0 /* successful return */
Listing 44.3 provides a template for the calling batch that might execute the stored
proce-dure shown in Listing 44.2 The main problem you need to solve is handling return codes
properly and responding with the correct transaction handling
LISTING 44.3 Template Code for a Calling Batch or Stored Procedure That Might Execute a
Stored Procedure Built with the Template in Listing 44.2
/* Retrieve status code to determine if proc was successful */
declare @status_val int, @trncnt int
select @trncnt = @@trancount — save @@trancount value
if @trncnt = 0 — transaction has not begun
begin tran t1 — begin tran increments nest level to 1
Trang 5else — otherwise, already in a transaction
save tran t1 — save tran doesn’t increment nest level
/* do some processing if required */
if (@@error != 0) — or other error condition
begin
rollback tran t1 — rollback to savepoint,or begin tran
return — and exit batch/procedure
end
execute @status_val = ptran1 —exec procedure, begin nesting
if @status_val = 25 — if proc performed rollback
begin — determine whether to rollback or continue
rollback tran t1
return
end
/* more processing if required */
if @trncnt = 0 — this proc/batch issued begin tran
commit tran t1 — commit tran, decrement @@trancount to 0
return — commit not required with save tran
Handling Errors in Stored Procedures
SQL Server 2008 provides the TRY CATCHconstruct, which you can use within your
T-SQL stored procedures to provide a more graceful mechanism for exception handling than
was available in previous versions of SQL Server by checking@@ERROR(and often the use of
GOTOstatements) after each SQL statement
ATRY CATCHconstruct consists of two parts: a TRYblock and a CATCHblock When an
error condition is detected in a T-SQL statement inside a TRYblock, control is immediately
passed to a CATCHblock, where the error is processed T-SQL statements in the TRYblock
that follow the statement that generated the error are not executed
If an error occurs and processing is passed to the CATCHblock, after the statements in the
CATCHblock are executed, control is then transferred to the first T-SQL statement that
follows the END CATCHstatement If there are no errors inside the TRYblock, control is
passed to the statement immediately after the associated END CATCHstatement, essentially
skipping over the statements in the CATCHblock
ATRYis initiated with the BEGIN TRYstatement and ended with the END TRYstatement
and can consist of one or more T-SQL statements between the BEGIN TRYandEND TRY
statements The TRYblock must be followed immediately by a CATCHblock A CATCHblock
Trang 6is indicated with the BEGIN CATCHstatement and ended with the END CATCHstatement and
can consist of one or more SQL statements In SQL Server, each TRYblock can be
associ-ated with only one CATCHblock
The syntax of the TRY CATCHconstruct is as follows:
BEGIN TRY
one_or_more_sql_statements
END TRY
BEGIN CATCH
one_or_more_sql_statements
END CATCH
When in a CATCHblock, you can use the following error functions to capture information
about the error that invoked the CATCHblock:
error occurred
Unlike@@error, which is reset by each statement that is executed, the error information
retrieved by the error functions remains constant anywhere within the scope of the CATCH
block of a TRY CATCHconstruct Error functions can also be referenced inside a stored
procedure and can be used to retrieve error information when the stored procedure is
executed within a CATCHblock This allows you to modularize the error handling into a
single procedure so you do not have to repeat the error-handling code in every CATCH
block Listing 44.4 shows an example of an error-handling procedure that you can use in
yourCATCHblocks
LISTING 44.4 An Example of a Standard Error-Handling Procedure
create proc dbo.error_handler
as
begin
Declare @errnum int,
@severity int,
@errstate int,
@proc nvarchar(126),
@line int,
@message nvarchar(4000)
capture the error information that caused the CATCH block to be invoked
Trang 7SELECT @errnum = ERROR_NUMBER(),
@severity = ERROR_SEVERITY(),
@errstate = ERROR_STATE(),
@proc = ERROR_PROCEDURE(),
@line = ERROR_LINE(),
@message = ERROR_MESSAGE()
raise an error message with information on the error
RAISERROR (‘Failed to add new publisher for the following reason:
Error: %d, Severity: %d, State: %d, in proc %s at line %d, Message: “%s”’,
16, 1, @errnum, @severity, @errstate, @proc, @line, @message) Return
end
Listing 44.5 provides an example of the TRY CATCHconstruct in a stored procedure,
modifying the insert_publishersprocedure created in Listing 44.1 Note that this CATCH
block uses the dbo.error_handlerprocedure defined in Listing 44.4
LISTING 44.5 Using a TRY CATCH Construct for Error Handling in a Stored Procedure
use bigpubs2008
go
alter proc insert_publishers @pub_id char(4),
@pub_name varchar(40),
@city varchar(20),
@state char(2),
@country varchar(30) as
BEGIN TRY
INSERT INTO bigpubs2008.dbo.publishers
(pub_id, pub_name, city, state, country)
VALUES(@pub_id, @pub_name, @city, @state, @country)
if no error occurs, we should see this print statement
print ‘New Publisher added’
END TRY
BEGIN CATCH
invoke the error_handler procedure
exec error_handler
return a non-zero status code
RETURN -101
END CATCH
if successful execution, return 0
RETURN 0
go
exec insert_publishers ‘9951’, ‘Pearson Education’, ‘Indianapolis’, ‘IN’, ‘USA’
Trang 8exec insert_publishers ‘9950’, ‘Sams Publishing’, ‘Indianapolis’, ‘IN’, ‘USA’
go
New Publisher added
Msg 50000, Level 16, State 1, Procedure insert_publishers, Line 18
Failed to add new publisher for the following reason:
Error: 2627, Severity: 14, State: 1, in proc insert_publishers at line 8,
Message: “Violation of PRIMARY KEY constraint ‘UPKCL_pubind’ Cannot insert
duplicate key in object ‘dbo.publishers’.”
If you want to capture and handle any errors that may occur within a CATCHblock, you
can incorporate another TRY CATCHblock within the CATCHblock itself
Also note that some errors with severity 20 or higher that cause SQL Server to close the
user connection cannot be handled by theTRY CATCHconstruct However, severity level
20 or higher errors that do not result in the connection being closed are captured and
handled by theCATCHblock Any errors with a severity level of 10 or less are considered
warnings or informational messages and not really errors and thus are not handled by the
TRY CATCHconstruct Also, any compile errors (such as syntax errors) or object name
resolution errors that happen during deferred name resolution also do not invoke aCATCH
block These errors are returned to the application or batch that called the
error-generat-ing routine
Using Source Code Control with Stored Procedures
When you can, it’s generally a good idea to use source code control for your stored
proce-dure scripts Stored proceproce-dures are as much a part of an application as the application
code itself and should be treated as such When using source code control, you can link
versions of your procedures and other object creation scripts with specific versions of
your applications Using source code control systems also provides a great way to keep
track of the changes to your stored procedures and other object creation scripts, enabling
you to go back to a previous version if the modifications lead to problems with the
appli-cations or data
SQL Server Management Studio (SSMS) provides a feature similar to Visual Studio that lets
you organize your SQL scripts into solutions and projects A project is a collection of one or
more script files stored in the Windows file system, usually in a folder with the same
name as the project A solution is a collection of one or more projects.
In addition to providing a way to manage and organize your scripts, SSMS can also
inte-grate with source code control software if the source code control system provides a
compatible plug-in If you are using Visual Studio, it’s likely that you are also using Visual
SourceSafe Visual SourceSafe provides a one-to-one mapping between SSMS projects and
Visual SourceSafe projects After you create an SSMS solution, you can check the entire
SSMS solution into Visual SourceSafe and then check out individual script files or projects
Trang 9FIGURE 44.1 Creating a new project/solution and adding it to source control
You can also specify that a solution be added to source code control when you create a
new solution In SSMS, you select File, New and then select New Project In the New
Project dialog, you can specify the name for the project and solution, and you can also
specify whether to add the solution to source code control, as shown in Figure 44.1
When you add a solution to Visual SourceSafe, it prompts you for the login ID and
pass-word to use to access Visual SourceSafe After you provide that information, Visual
SourceSafe then prompts you for the Visual SourceSafe project to add the SMSS project to,
or it allows you to create a new project in Visual SourceSafe
Within a project, you can specify the database connection(s) for the project and add SQL
script files to the Queriesfolder After creating a new script file, you can add it into the
source code control system by right-clicking the script file in the Solutions Explorer and
selecting Check In (see Figure 44.2)
After you check in a script file, you can right-click the file and perform source code
control tasks such as checking out the script for editing, getting the current version,
comparing versions, and viewing the check-in history If you check out the script for
editing, you can then open it in a new query window, where you can make changes to the
script and then execute it in the database When you are satisfied with the changes, you
can check the new version back into the source code control system
For more information on working with solutions and projects in SSMS, see Chapter 4,
“SQL Server Management Studio.”
Trang 10FIGURE 44.2 Checking in a new script file
Using Cursors in Stored Procedures
When using cursors in stored procedures in SQL Server, you need to be aware of the
scope of the cursor and how it can be accessed within calling or called procedures
Cursors in SQL Server can be declared as local or global A global cursor defined in a
stored procedure is available until it is deallocated or when the connection closes A local
cursor goes out of scope when the stored procedure that declared it terminates or the
procedure scope changes
If neither the GLOBALnorLOCALoption is specified when the cursor is declared in a stored
procedure, the default cursor type is determined by the database option CURSOR_DEFAULT,
which you set with the ALTER DATABASEstatement The default value for the option
is_local_cursor_defaultisFALSE, which defaults cursors as global, to match the
behav-ior of earlier versions of SQL Server If this value in the sys.databasescatalog view is set
toTRUE, T-SQL cursors default to local cursors
TIP
If neither GLOBALnorLOCALis specified, the default scope setting for cursors in SQL
Server 2008 is determined by the Default to Local Cursor database option This option
currently defaults to OFFto provide backward compatibility with versions of SQL Server
prior to 7.0, in which all cursors were global This default setting might change in
future versions, so it is recommended that you explicitly specify the LOCALorGLOBAL
option when declaring your cursors so your code will not be affected by changes to the
default setting