ptg 1724 CHAPTER 43 Transact-SQL Programming Guidelines, Tips, and Tricks The following example is similar to the query in Listing 43.58 but replaces CROSS APPLY with the OUTER APPLY clause: select st.stor_id, stor_name, s.ord_date, s.qty from stores st outer apply dbo.fn_GetTopOrders (st.stor_id, 3) as s where st.state = ‘MI’ and st.stor_name in (‘Barnes & Noble’, ‘B Dalton BookSeller’, ‘Waldenbooks’) order by stor_id, s.qty DESC go stor_id stor_name ord_date qty B251 B Dalton Bookseller 2008-01-23 00:00:00.000 1740 B251 B Dalton Bookseller 2007-11-04 00:00:00.000 1704 B251 B Dalton Bookseller 2008-02-22 00:00:00.000 1560 B510 Barnes & Noble 2008-08-13 00:00:00.000 1464 B510 Barnes & Noble 2007-10-08 00:00:00.000 1200 B510 Barnes & Noble 2006-01-08 00:00:00.000 924 B511 Barnes & Noble NULL NULL P963 Waldenbooks 2008-07-07 00:00:00.000 1668 P963 Waldenbooks 2006-12-30 00:00:00.000 1068 P963 Waldenbooks 2006-03-29 00:00:00.000 1032 Q017 Waldenbooks 2007-11-02 00:00:00.000 1776 Q017 Waldenbooks 2006-06-15 00:00:00.000 1704 Q017 Waldenbooks 2007-02-24 00:00:00.000 1548 TRY CATCH Logic for Error Handling SQL Server 2005 also introduced the TRY CATCH construct, which you can use within T- SQL code to provide a more graceful mechanism for exception handling than was avail- able in previous versions of SQL Server. In versions prior to 2005, error handling was typically done by checking @@ERROR after each SQL statement and often using the GOTO statement to branch to an error-handling routine. A TRY CATCH construct consists of two parts: a TRY block and CATCH block. When an error condition is detected in a T-SQL statement that is inside a TRY block, control is immediately passed to the CATCH block, where the error is processed. T-SQL statements in the TRY block that follow the statement that generated the error are not executed. If an error occurs and processing is passed to the CATCH block, after the statements in the CATCH block are executed, control is transferred to the first T-SQL statement that follows the END CATCH statement. If there are no errors inside the TRY block, control is passed to the statement immediately after the associated END CATCH statement, essentially skipping over the statements in the CATCH block. ptg 1725 TRY CATCH Logic for Error Handling 43 A TRY block is initiated with the BEGIN TRY statement and ended with the END TRY state- ment and can consist of one or more Transact-SQL statements between the BEGIN TRY and END TRY statements. The TRY block must be followed immediately by a CATCH block. A CATCH block is indicated with the BEGIN CATCH statement and ended with the END CATCH statement and can consist of one or more SQL statements. In SQL Server, each TRY block can be associated with only one CATCH block. The syntax of the TRY CATCH construct is as follows: BEGIN TRY one_or_more_sql_statements END TRY BEGIN CATCH one_or_more_sql_statements END CATCH In a CATCH block, you can use the following error functions to capture information about the error that invoked the CATCH block: . ERROR_NUMBER()—Returns the error number . ERROR_MESSAGE()—Returns the complete text of the error message . ERROR_SEVERITY()—Returns the error severity . ERROR_STATE()—Returns the error state number . ERROR_LINE()—Returns the line number inside the procedure that caused the error . ERROR_PROCEDURE()—Returns the name of the stored procedure or trigger where the 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 CATCH construct. Error functions can also be referenced from within a stored procedure invoked within a CATCH block. This allows you to modularize the error handling into a single stored procedure so you do not have to repeat the error-handling code in every CATCH block. Listing 43.59 shows an example of an error-handling procedure that you can use in your CATCH blocks. LISTING 43.59 An Example of a Standard Error Handler Procedure use bigpubs2008 go create proc dbo.error_handler as begin Declare @errnum int, @severity int, @errstate int, ptg 1726 CHAPTER 43 Transact-SQL Programming Guidelines, Tips, and Tricks @proc nvarchar(126), @line int, @message nvarchar(4000) capture the error information that caused the CATCH block to be invoked SELECT @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 43.60 provides an example of the use of the TRY CATCH construct in a T-SQL batch. Note that this CATCH block uses the dbo.error_handler procedure defined in Listing 43.59. LISTING 43.60 Using a TRY CATCH Construct for Error Handling in a T-SQL Batch use bigpubs2008 go BEGIN TRY INSERT INTO bigpubs2008.dbo.publishers (pub_id, pub_name, city, state, country) VALUES(‘9950’, ‘Sams Publishing’, ‘Indianapolis’, ‘IN’, ‘USA’) 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 END CATCH if successful execution, return 0 go Msg 50000, Level 16, State 1, Procedure error_handler, Line 18 Failed to add new publisher for the following reason: Error: 2627, Severity: 14, State: 1, in proc (null) at line 2, Message: “Violation of PRIMARY KEY constraint ‘UPKCL_pubind’. Cannot insert duplicate key in object ‘dbo.publishers’.” ptg 1727 The TABLESAMPLE Clause 43 If you want to capture and handle any errors that may occur within a CATCH block, you can incorporate another TRY CATCH block within the CATCH block. NOTE Some errors with severity 20 or higher that would cause SQL Server to close the user connection cannot be handled by the TRY CATCH construct. However, severity level 20 or higher errors that do not result in the connection being closed can be captured and handled by the CATCH block. Any errors with a severity level of 10 or less are con- sidered only warnings or informational messages and not really errors, and thus they are not handled by the TRY CATCH construct. Also, any compile errors (such as syn- tax errors) or object name resolution errors that happen during deferred name resolu- tion also do not invoke a CATCH block. These errors are returned to the application or batch that called the error-generating routine. The TABLESAMPLE Clause The TABLESAMPLE clause lets you query a random sample of data from a table (either an exact number of rows or a percentage of rows). You can use TABLESAMPLE to quickly return a sample from a large table when the sample does not have to be a truly random sample at the level of individual rows. This clause is also useful when you want to test your code against a random subset of data that you copy from a production environment or when you just want to test the validity of your solutions against a subset of data as opposed to the entire data set. To return a random sample of data using the TABLESAMPLE clause, you specify it in a query’s FROM clause, right after the table name or table alias. The TABLESAMPLE clause has the following syntax: TABLESAMPLE [SYSTEM] (sample_number [ PERCENT | ROWS ] ) [ REPEATABLE (repeat_seed) ] Specifying the SYSTEM keyword is optional, but this option is currently the only sampling method available in SQL Server and is applied by default. SYSTEM specifies that an ANSI SQL implementation-dependent sampling method will be used. This means that individ- ual database management system (DBMS) products can implement this method differ- ently. In SQL Server, the same sampling method that it uses to sample data to create statistics is used to generate the results for TABLESAMPLE. The result set returned by a query using TABLESAMPLE and a specified percentage is created by generating a random value for each physical page in the table. Based on the random value generated for a page, that page is either included in the sample or excluded. When a page is included in the sample, all rows on that page are returned in the result set. For ptg 1728 CHAPTER 43 Transact-SQL Programming Guidelines, Tips, and Tricks example, if you specify TABLESAMPLE SYSTEM 10 PERCENT, SQL Server returns all the rows from approximately 10% of the randomly selected data pages of the table. When a specific number of rows rather than a percentage is specified, the requested number of rows is converted into a percentage of the total number of rows in the table and a percentage of the number of pages that should be returned. The TABLESAMPLE opera- tion is then performed against the computed percentage of pages. If the rows are evenly distributed on the pages of the table, the number of rows returned by a TABLESAMPLE query should be close to the requested sample size. However, if there is a mix of full and sparse pages in the table, the number of rows returned may vary widely for subsequent executions of the query. Consider the following query: with sales_sample as ( select * from sales TABLESAMPLE (1 percent) ) select count(*) as numrows from sales_sample go numrows 2055 There are 168,715 rows in the sales table in the bigpubs2008 database. A 1% sample should return approximately 1,687 rows. However, as you can see from the preceding example, it returns 2,055 rows. NOTE Each time this query is run, it is likely to return a different set of rows, so your row counts may not match those presented in these examples. If you invoke the query again, it could return a different number of rows: with sales_sample as ( select * from sales TABLESAMPLE (1 percent) ) select count(*) as numrows from sales_sample go numrows 1138 Note also that if you specify an actual number of rows, because the sampling is done at the page level, the pages sampled may have more or fewer rows than required to provide the requested sample size. For example, consider the following query, which requests a TABLESAMPLE of 1,000 rows: ptg 1729 The TABLESAMPLE Clause 43 with sales_sample as ( select * from sales TABLESAMPLE (1000 rows) ) select count(*) as numrows from sales_sample go numrows 683 A subsequent execution of the same query could return a different number of rows: with sales_sample as ( select * from sales TABLESAMPLE (1000 rows) ) select count(*) as numrows from sales_sample go numrows 1371 If you run this query repeatedly, you are likely to get a different number of rows every time. However, the larger the table and the greater the number of rows you request, the more likely it is to get a closer percentage or number of requested rows returned. The smaller the table and the smaller the number or percentage of rows you request, the less likely the query is to return the number of rows close to the number or percentage you requested. With very small tables, you might not even get any rows. To increase the likelihood of receiving the number of rows that you request, you should specify a greater number of rows than you actually need in the TABLESAMPLE clause and use the TOP option to specify the number of rows you actually want. For example, if you want a set of 1,000 random rows, you should request 2,000 rows in the TABLESAMPLE clause and then limit it to 1,000 rows with the TOP option, as in this example: select top 1000 * from sales TABLESAMPLE (2000 rows) When you do this, you may still get fewer than 1,000 rows returned, but the likelihood of that occurring is lower than if you request 1,000 rows in the TABLESAMPLE clause alone. Also, by specifying TOP(1000), you’re guaranteed not to get more than 1,000 rows. When you use a combination of TABLESAMPLE and TOP, the data you obtain is a more representa- tive sampling of the data in your table than if you use TOP alone. If you want to generate the same random sample each time you use the TABLESAMPLE clause, you can specify the REPEATABLE option with a specified repeat_seed value. The REPEATABLE option causes a selected sample to be returned again. When REPEATABLE is specified with the same repeat_seed value, SQL Server returns the same subset of rows, as ptg 1730 CHAPTER 43 Transact-SQL Programming Guidelines, Tips, and Tricks long as no changes have been made to the table. For example, the following query uses repeat_seed of 1 and, in this case, returns 16,896 rows: with sales_sample as ( select * from sales TABLESAMPLE (10 percent) repeatable (1) ) select count(*) as numrows from sales_sample go numrows 16896 When REPEATABLE is specified with a different repeat_seed value, SQL Server typically returns a different sample of the rows in the table. For example, the following query uses repeat_seed of 2 and gets a different set and number of rows: with sales_sample as ( select * from sales TABLESAMPLE (10 percent) repeatable (2) ) select count(*) as numrows from sales_sample go numrows 19856 Running the query again with repeat_seed of 1 returns the same result rows as previously: with sales_sample as ( select * from sales TABLESAMPLE (10 percent) repeatable (1) ) select count(*) as numrows from sales_sample go numrows 16896 The types of actions that are considered changes and could affect the repeatability of the TABLESAMPLE results include inserts, updates, deletes, index rebuilding, index defragment- ing, restoration of a database, and attachment of a database. You can use other techniques to request random data samples, but most of those tech- niques require scanning the entire table, which can be time-consuming and I/O intensive for very large tables. Using TABLESAMPLE for a specific table limits the Query Optimizer to performing table scans only on that table, but physical I/Os are performed only on the actual sampled pages included in the result set. Because of this, using TABLESAMPLE is usually a faster way of generating a random sampling of your data. ptg 1731 Summary 43 Summary Transact-SQL is a powerful data access and data modification language that provides a number of features and components to help you develop powerful and robust SQL Server–based applications. SQL Server 2005 further expanded the power and capabilities of T-SQL with the addition of a number of new features, and SQL Server 2008 provides even more (see Chapter 42 for details on the new T-SQL features introduced in SQL Server 2008). The guidelines, tips, and T-SQL features presented in this chapter provide you with some building blocks you can use to get the most out of your T-SQL code to help make your SQL Server–based applications even more powerful and robust. ptg This page intentionally left blank ptg CHAPTER 44 Advanced Stored Procedure Programming and Optimization IN THIS CHAPTER . T-SQL Stored Procedure Coding Guidelines . Using Cursors in Stored Procedures . Nested Stored Procedures . Using Temporary Tables in Stored Procedures . Using Remote Stored Procedures . Stored Procedure Performance . Using Dynamic SQL in Stored Procedures . Installing and Using .NET CLR Stored Procedures . Using Extended Stored Procedures Chapter 28, “Creating and Managing Stored Procedures,” introduced the basic syntax and features for creating, viewing, and maintaining stored procedures. This chapter delves into more advanced stored procedure features and programming methods and provides guidelines on how to develop more robust and better optimized stored proce- dures. It is important to understand the various capabilities and limitations of stored procedures before writing much stored procedure code. Poorly written procedures can make the server appear to run sluggishly and inefficiently. Well- written procedures run efficiently and solidly. Following the guidelines and tips presented in this chapter should help you write more efficient and robust stored procedures. T-SQL Stored Procedure Coding Guidelines Transact-SQL (T-SQL) stored procedures should be treated just like reusable application code. You should follow these suggested guidelines to ensure that your stored procedures are solid and robust: . Check all parameters for valid values and return an error message if a problem exists. . Be sure that the parameter data types match the column data types they are compared against to avoid data type mismatches and poor query optimization. . capabilities of T -SQL with the addition of a number of new features, and SQL Server 2008 provides even more (see Chapter 42 for details on the new T -SQL features introduced in SQL Server 2008) . The. data. ptg 1731 Summary 43 Summary Transact -SQL is a powerful data access and data modification language that provides a number of features and components to help you develop powerful and robust SQL Server based applications. SQL Server. one or more SQL statements. In SQL Server, each TRY block can be associated with only one CATCH block. The syntax of the TRY CATCH construct is as follows: BEGIN TRY one_or_more _sql_ statements END