1. Trang chủ
  2. » Công Nghệ Thông Tin

Microsoft SQL Server 2008 R2 Unleashed- P180 potx

10 208 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 174,43 KB

Nội dung

ptg 1744 CHAPTER 44 Advanced Stored Procedure Programming and Optimization If stored procedures are nested, they can access cursors declared in higher-level stored procedures in the call tree, but only if the cursors are declared as global. If the cursor is declared as local, it can be referenced only within the scope of the stored procedure in which it is declared. It cannot be accessed from a called or calling procedure. If the cursor is declared as global, it can be accessed within the declaring procedure, in a called proce- dure, or even from outside the declaring procedure if the cursor is not deallocated before the procedure returns. In the following example, procedure p1 creates a cursor defined as global that can then be accessed by procedure p2: if object_id(‘p1’) is not null drop proc p1 go if object_id(‘p2’) is not null drop proc p2 go create proc p2 as set nocount on fetch from global cursor defined in calling proc p1 fetch c1 return go create proc p1 as set nocount on Declare global cursor declare c1 cursor global for select title_id, type from titles open c1 fetch c1 exec p2 close c1 deallocate c1 go exec p1 go title_id type BI0194 biography ptg 1745 Using Cursors in Stored Procedures 44 title_id type BI1408 biography As you can see in the preceding example, the cursor c1 is defined as global in procedure p1 and can be accessed from within procedure p2. TIP To clean up the output when using cursors within stored procedures, specify the set nocount on option within the stored procedure to disable the n rows(s) affected that would normally be displayed after each invocation of the fetch statement. Now, look what happens if you modify procedure p1 to declare the cursor as local: alter proc p1 as set nocount on Declare local cursor declare c1 cursor local for select title_id, type from titles open c1 fetch c1 exec p2 close c1 deallocate c1 go exec p1 go title_id type BI0194 biography Msg 16916, Level 16, State 1, Procedure p2, Line 5 A cursor with the name ‘c1’ does not exist. Notice in this example that the cursor c1 is not available to the procedure p2. The reason is that the cursor is defined as local and is accessible only within the scope of procedure p1. Because the cursor is localized to the scope of p1, you are able to define a cursor with the same name within the scope of procedure p2: alter proc p2 as set nocount on ptg 1746 CHAPTER 44 Advanced Stored Procedure Programming and Optimization Declare another local cursor with same name ‘c1’ declare c1 cursor local for select au_id, au_lname from authors open c1 fetch c1 close c1 deallocate c1 return go exec p1 go title_id type BI0194 biography au_id au_lname 047-43-0360 Michener Notice that when you define the scope of both cursors as local, each procedure can create a cursor with the same name without any conflict between them. You can take advantage of this feature if you have a recursive stored procedure that uses a cursor, as demonstrated in the “Recursive Stored Procedures” section, later in this chapter. In addition to a global cursor defined in a calling procedure being available within a called procedure, the reverse is possible as well. A global cursor defined in a called procedure is available to the calling procedure as demonstrated in the following example: alter proc p2 as set nocount on declare global cursor c2 declare c2 cursor global for select au_id, au_lname from authors open c2 do not close/deallocate cursor so it can be used by calling proc p1 return go alter proc p1 as set nocount on declare c1 cursor local for select title_id, type from titles open c1 fetch c1 exec p2 ptg 1747 Using Cursors in Stored Procedures 44 fetch from global cursor declared in proc p2 fetch c2 close c1 deallocate c1 close c2 deallocate c2 return go exec p1 go title_id type BI0194 biography au_id au_lname 047-43-0360 Michener As you can see in the preceding example, the global cursor defined in the called procedure p2 is available for use by the calling procedure p1 as long as the cursor is left open by the called procedure p2. NOTE Remember that global cursors persist beyond the scope of the procedure in which they are defined. If you are going to declare global cursors in called procedures to be accessed by the calling procedure, be sure the calling procedure closes and deallo- cates the cursor declared in the called procedure before it returns. Otherwise, the cursor will remain open and defined until the end of the user session. The following example demonstrates this behavior: alter proc p1 as set nocount on declare c1 cursor local for select title_id, type from titles open c1 fetch c1 exec p2 fetch from global cursor declared in proc p2 fetch c2 close c1 deallocate c1 ptg 1748 CHAPTER 44 Advanced Stored Procedure Programming and Optimization Cursor c2 is not closed/deallocated before return return go exec p1 go Cursor c2 should still be open here so the following fetch will work fetch c2 go title_id type BI0194 biography au_id au_lname 047-43-0360 Michener au_id au_lname 052-04-3539 Gray Using CURSOR Variables in Stored Procedures Another method available in SQL Server 2008 for passing cursor result sets between stored procedures is using the cursor data type. The cursor data type can be used to bind a cursor result set to a local variable, and that variable can then be used to manage and access the cursor result set. Cursor variables can be referenced in any of the cursor management statements: OPEN, FETCH, CLOSE, and DEALLOCATE. A stored procedure can pass cursor variables as output parameters only; cursor variables cannot be passed as input parameters. When defining a CURSOR output parameter, you must also specify the VARYING keyword. When assigning a cursor to a cursor variable, you must use the SET command because an assignment select is not allowed. Cursor data types can either be the source or the target in a SET statement. The following stored procedure declares a cursor, opens it, and passes it back as an output parameter using the cursor data type: IF EXISTS ( SELECT * FROM sys.procedures WHERE schema_id = schema_id(‘dbo’) ptg 1749 Using Cursors in Stored Procedures 44 AND name = N’cursor_proc’) DROP PROCEDURE dbo.cursor_proc GO create proc cursor_proc @cursor CURSOR VARYING OUTPUT as declare curs1 cursor global for select cast(title as varchar(30)) as title , pubdate from titles set @cursor = curs1 open curs1 return A cursor variable and the declared cursor name can be used interchangeably in cursor commands. You can use either the variable name or declared name to open, fetch, close, and deallocate the cursor. Fetching using either the cursor name or cursor variable fetches the next row in the cursor result set. Listing 44.6 illustrates how each fetch gets the next row in the result set. LISTING 44.6 Fetching Cursor Rows by Using the Declared Cursor Name and a Cursor Variable set nocount on declare @curs CURSOR exec cursor_proc @cursor = @curs output fetch curs1 fetch @curs fetch curs1 fetch @curs go title pubdate Samuel Johnson 2008-09-19 00:00:00.000 title pubdate Freud, Dora, and Vienna 1900 2008-02-25 00:00:00.000 title pubdate Freud: A Life for Our Time 2008-06-21 00:00:00.000 title pubdate For Love of the World 2006-01-06 00:00:00.000 ptg 1750 CHAPTER 44 Advanced Stored Procedure Programming and Optimization One of the problems with a cursor declared as a global cursor in the procedure is that you cannot invoke the procedure again within the same session unless the cursor is closed and deallocated. This can be a problem if you need to get the cursor into a cursor variable again. If you try to invoke the procedure again, and the cursor hasn’t been closed or deal- located, you get error messages, as shown in the following example: set nocount on declare @curs CURSOR exec cursor_proc @cursor = @curs output go Msg 16915, Level 16, State 1, Procedure cursor_proc, Line 4 A cursor with the name ‘curs1’ already exists. Msg 16905, Level 16, State 1, Procedure cursor_proc, Line 6 The cursor is already open. close curs1 deallocate curs1 go One way to work around this issue is to use the CURSOR_STATUS function in the procedure to check whether the cursor exists yet before declaring it and also to check whether the cursor is already open before opening it. Thus, the stored procedure declares the cursor only if it doesn’t exist and opens the cursor only if it’s closed, but the stored procedure always returns the cursor in the cursor output parameter. Keeping this in mind, take a look at a revised version of the cursor_proc stored procedure: IF EXISTS ( SELECT * FROM sys.procedures WHERE schema_id = schema_id(‘dbo’) AND name = N’cursor_proc’) DROP PROCEDURE dbo.cursor_proc GO go create proc cursor_proc @cursor CURSOR VARYING OUTPUT as if CURSOR_STATUS(‘global’, ‘curs1’) = -3 cursor does not exist declare curs1 cursor global for select cast(title as varchar(30)) as title , pubdate from titles if CURSOR_STATUS(‘global’, ‘curs1’) = -1 cursor is not open open curs1 set @cursor = curs1 return When the procedure is written this way, you can now safely call the procedure at any time, even if the cursor is already open. If the cursor is open, it simply stores the cursor pointer into the cursor variable. ptg 1751 Using Cursors in Stored Procedures 44 If you want to close the cursor, you can do so by using either the cursor variable or declared cursor name. When it is closed, however, you cannot fetch more rows from the cursor or cursor variable until it is reopened: set nocount on declare @curs CURSOR exec cursor_proc @cursor = @curs output fetch curs1 fetch @curs close the cursor close curs1 try to fetch from the cursor variable fetch @curs go title pubdate Samuel Johnson 2008-09-19 00:00:00.000 title pubdate Freud, Dora, and Vienna 1900 2008-02-25 00:00:00.000 Msg 16917, Level 16, State 2, Line 7 Cursor is not open. However, if the cursor has been assigned to a cursor variable, it cannot be fully deallocated until the last remaining reference to the cursor issues the DEALLOCATE command. Until all references to the cursor issue the DEALLOCATE command, the cursor can be reopened, but only by using the remaining cursor reference(s) that hasn’t issued the DEALLOCATE command. An example of this behavior is shown in Listing 44.7. If the cursor has not been closed, only the last deallocation of the cursor closes it. LISTING 44.7 Deallocating a Cursor by Cursor Name and Cursor Variable declare @curs CURSOR exec cursor_proc @cursor = @curs output print ‘FETCH VIA NAME:’ fetch curs1 print ‘FETCH VIA VARIABLE:’ fetch @curs print ‘CLOSE BY NAME’ close curs1 print ‘DEALLOCATE BY NAME’ ptg 1752 CHAPTER 44 Advanced Stored Procedure Programming and Optimization deallocate curs1 print ‘ATTEMPT FETCH VIA VARIABLE (CURSOR SHOULD BE CLOSED):’ fetch @curs print ‘ATTEMPT TO OPEN VIA VARIABLE (CURSOR SHOULD OPEN, NOT DEALLOCATED YET)’ open @curs print ‘ATTEMPT FETCH VIA VARIABLE (SHOULD START FROM BEGINNING AGAIN):’ fetch @curs print ‘CLOSE AND DEALLOCATE VIA VARIABLE’ close @curs deallocate @curs print ‘ATTEMPT TO OPEN VIA VARIABLE (SHOULD FAIL, SINCE NOW FULLY DEALLOCATED):’ open @curs go FETCH VIA NAME: TITLE PUBDATE SAMUEL JOHNSON 2008-09-19 00:00:00.000 FETCH VIA VARIABLE: TITLE PUBDATE FREUD, DORA, AND VIENNA 1900 2008-02-25 00:00:00.000 CLOSE BY NAME DEALLOCATE BY NAME ATTEMPT FETCH VIA VARIABLE (CURSOR SHOULD BE CLOSED): MSG 16917, LEVEL 16, STATE 2, LINE 15 CURSOR IS NOT OPEN. ATTEMPT TO OPEN VIA VARIABLE (CURSOR SHOULD OPEN, NOT DEALLOCATED YET) ATTEMPT FETCH VIA VARIABLE (SHOULD START FROM BEGINNING AGAIN): TITLE PUBDATE SAMUEL JOHNSON 2008-09-19 00:00:00.000 CLOSE AND DEALLOCATE VIA VARIABLE ATTEMPT TO OPEN VIA VARIABLE (SHOULD FAIL, SINCE NOW FULLY DEALLOCATED): MSG 16950, LEVEL 16, STATE 2, LINE 27 The variable ‘@curs’ does not currently have a cursor allocated to it. ptg 1753 Nested Stored Procedures 44 If the cursor is declared as a local cursor within a stored procedure, it can still be passed back in an output variable to a cursor variable, but it is accessible only through the cursor variable, as shown in Listing 44.8. LISTING 44.8 Assigning a Local Cursor to a Cursor Output Parameter IF EXISTS ( SELECT * FROM sys.procedures WHERE schema_id = schema_id(‘dbo’) AND name = N’cursor_proc2’) DROP PROCEDURE dbo.cursor_proc2 GO create proc cursor_proc2 @cursor CURSOR varying output as declare curs1 cursor local for select cast(title as varchar(30)) as title , pubdate from titles set @cursor = curs1 open curs1 go declare @curs CURSOR exec cursor_proc2 @cursor = @curs output print ‘ATTEMPT FETCH VIA NAME:’ fetch next from curs1 print ‘ATTEMPT FETCH VIA VARIABLE:’ fetch next from @curs go ATTEMPT FETCH VIA NAME: Msg 16916, Level 16, State 1, Line 4 A cursor with the name ‘curs1’ does not exist. ATTEMPT FETCH VIA VARIABLE: title pubdate Samuel Johnson 2008-09-19 00:00:00.000 Nested Stored Procedures Stored procedures can call other stored procedures, and any of those procedures can call other procedures, up to a maximum nesting level of 32 levels deep. If you exceed the 32- level nesting limit, an error message is raised, the batch is aborted, and any open transac- tion in the session is rolled back. The nesting level limit prevents a recursive procedure from calling itself repeatedly in an infinite loop until a stack overflow occurs. To check the depth to which a procedure is nested, you use the system function @@NESTLEVEL (see Listing 44.9). . 052-04-3539 Gray Using CURSOR Variables in Stored Procedures Another method available in SQL Server 2008 for passing cursor result sets between stored procedures is using the cursor data type @curs go title pubdate Samuel Johnson 2008- 09-19 00:00:00.000 title pubdate Freud, Dora, and Vienna 1900 2008- 02-25 00:00:00.000 title pubdate Freud: A Life for Our Time 2008- 06-21 00:00:00.000 title. cursor variable fetch @curs go title pubdate Samuel Johnson 2008- 09-19 00:00:00.000 title pubdate Freud, Dora, and Vienna 1900 2008- 02-25 00:00:00.000 Msg 16917, Level 16, State 2, Line 7 Cursor

Ngày đăng: 05/07/2014, 02:20

TỪ KHÓA LIÊN QUAN