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

Microsoft SQL Server 2008 R2 Unleashed- P180 potx

10 209 0
Tài liệu đã được kiểm tra trùng lặp

Đ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

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

In the following example, procedure p1creates 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 n

Trang 1

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 p1creates 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

Trang 2

title_id type

-

-BI1408 biography

As you can see in the preceding example, the cursor c1is defined as global in procedure

p1and can be accessed from within procedure p2.

TIP

To clean up the output when using cursors within stored procedures, specify the set

nocount onoption within the stored procedure to disable the n rows(s) affected

that would normally be displayed after each invocation of the fetchstatement.

Now, look what happens if you modify procedure p1to 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 c1is 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

Trang 3

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

Trang 4

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

p2is available for use by the calling procedure p1as 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

Trang 5

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 CURSORoutput parameter, you

must also specify the VARYINGkeyword.

When assigning a cursor to a cursor variable, you must use the SETcommand because an

assignmentselectis not allowed Cursor data types can either be the source or the target

in a SETstatement.

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’)

Trang 6

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

Trang 7

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 theCURSOR_STATUSfunction 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_procstored 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.

Trang 8

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 DEALLOCATEcommand Until all

references to the cursor issue the DEALLOCATEcommand, 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’

Trang 9

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

Trang 10

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).

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

TỪ KHÓA LIÊN QUAN