3 4
A cursor is an entity that maps over a result set and establishes a position on a single row within the result set. After the cursor is positioned on a row, operations can be performed on that row or on a block of rows starting at that position. This lesson will introduce you to SQL Server cursors and to the three types of cursor implementations. You will also learn how to fetch (retrieve) a row from a cursor and how to specify the behavior of a cursor. In addition, you will be introduced to cursor locking.
Operations in a relational database act on a complete set of rows. The set of rows returned by a SELECT statement consists of all the rows that satisfy the conditions in the WHERE clause of the statement. This complete set of rows returned by the statement is known as the result set. Applications—especially interactive, online applications—cannot always work effectively with the entire result set as a unit. These applications need a mechanism to work with one row or with a small block of rows at a time. Cursors are an extension to result sets that provide that mechanism.
Cursors extend result processing by supporting the following functionalities:
SQL Server supports three types of cursor implementations: Transact-SQL server cursors, API server cursors, and client cursors. Because Transact-SQL server cursors and API server cursors are implemented on the server, they are referred to collectively as server cursors.
Do not mix the use of these various types of cursors. If you execute a DECLARE CURSOR and OPEN statement from an application, first set the API cursor attributes to their defaults. If you set API cursor attributes to something other than their defaults and then execute a DECLARE CURSOR and OPEN statement, you are asking SQL Server to map an API cursor over a Transact-SQL cursor. For example, do not set the ODBC attributes that call for mapping a keyset-driven cursor over a result set and then use that statement handle to execute a DECLARE CURSOR and OPEN statement that calls for an INSENSITIVE cursor.
A potential drawback of server cursors is that they currently do not support all Transact-SQL statements. Server cursors do not support Transact-SQL statements that generate multiple result sets; therefore, they cannot be used when the application executes a stored procedure or a batch that contains more than one SELECT statement. Server cursors also do not support SQL statements containing the keywords COMPUTE, COMPUTE BY, FOR BROWSE, or INTO.
Transact-SQL Server cursors are based on the DECLARE CURSOR statement and are used mainly in Transact-SQL scripts, stored procedures, and triggers. Transact-SQL cursors are implemented on the server and are managed by Transact-SQL statements sent from the client to the server. They are also contained in batches, stored procedures, or triggers.
When working with Transact-SQL cursors, you use a set of Transact-SQL statements to declare, populate, and retrieve data (as outlined in the following steps):
The following set of Transact-SQL statements illustrates how to declare a cursor, populate that cursor, retrieve data from the result set, update that data, close the cursor, and deallocate the cursor:
/* Declares the AuthorsCursor cursor and associates the cursor with a SELECT statement. */ USE Pubs DECLARE AuthorsCursor CURSOR FOR SELECT * FROM Authors ORDER BY Au_lname /* Populates the AuthorsCursor cursor with the result set from the SELECT statement. */ OPEN AuthorsCursor /* Retrieves the first row from the result set. */ FETCH NEXT FROM AuthorsCursor /* Updates the phone number within the retrieved row. */ UPDATE Authors SET Phone = '415 658-9932' WHERE CURRENT OF AuthorsCursor /* Closes the AuthorsCursor cursor. */ CLOSE AuthorsCursor /* De-allocates the AuthorsCursor cursor. */ DEALLOCATE AuthorsCursor
Only Transact-SQL statements can reference Transact-SQL cursor names and variables. The API functions of OLE DB, ODBC, ADO, and DB-Library cannot reference Transact-SQL cursor names and variables. Applications that need cursor processing and are using these APIs should use the cursor support built into the database API, instead of Transact-SQL cursors.
You can use Transact-SQL cursors in applications by using FETCH and by binding each column returned by the FETCH to a program variable. The Transact-SQL FETCH does not support batches, however, so this method is the least efficient way to return data to an application: Fetching each row requires a round trip to the server. A more efficient way to fetch rows is to use the cursor functionality built into the database APIs that support batches.
Transact-SQL cursors are extremely efficient when contained in stored procedures and triggers. Everything is compiled into one execution plan on the server, and there is no network traffic associated with fetching rows.
API server cursors are cursors that are implemented on the server and are managed by API cursor functions. API server cursors support the API cursor functions in OLE DB, ODBC, and DB-Library. Each time a client application calls an API cursor function, the SQL Server OLE DB provider, ODBC driver, or DB-Library DLL transmits the request to the server for action against the API server cursor.
NOTE
The OLE DB, ODBC, ADO, and DB-Library APIs support mapping cursors over the result sets of executed SQL statements. The SQL Server OLE DB provider, SQL Server ODBC driver, and DB-Library DLL implement these operations through the use of API server cursors.
When using an API server cursor in OLE DB, ODBC, and ADO, use the functions or methods of the API to perform the following tasks:
In DB-Library, use the special DB-Library Cursor Library functions to work with an API server cursor.
If the API cursor attributes are set to their default settings, the SQL Server OLE DB provider and SQL Server ODBC driver use default result sets. Although the API is technically asking for a cursor, the default cursor characteristics match the behavior of a default result set. The OLE DB provider and ODBC driver, therefore, implement the default cursor options by using a default result set. This method is the most efficient way to retrieve rows from the server. When using default result sets, an application can execute any Transact-SQL statement or batch, but it can have only one outstanding statement on a connection. In other words, the application must process or cancel all of the result sets returned by one statement before it can execute another statement on the connection.
If the API cursor attributes are set to anything other than their defaults, the SQL Server OLE DB provider and the SQL Server ODBC driver use API server cursors instead of default result sets. Each call to an API function that fetches rows generates a round trip to the server to fetch the rows from the API server cursor.
DB-Library applications use the DB-Library Cursor Library functions to request cursors. If DBCLIENTCURSOR is not set, the DB-Library Cursor Library functions use API server cursors in the same way as the SQL Server OLE DB provider and SQL Server ODBC driver.
An application cannot execute the following statements when using API server cursors:
The SQL Server ODBC driver, the DB-Library DLL, and the ADO API DLL help implement client cursors internally. Client cursors are implemented by caching all of the client's result set rows. Each time a client application calls an API cursor function, the SQL Server ODBC driver, the DB-Library DLL, or the ADO DLL performs the cursor operation on the result set rows cached on the client.
In a client cursor, a default result set is used to cache the entire result set on the client, and all cursor operations are performed against this client cache. None of the server cursor functionality of SQL Server 2000 is used. Client cursors support only forward-only and static cursors, not keyset-driven or dynamic cursors.
The DB-Library client cursors were originally implemented before SQL Server supported server cursors. ODBC implements client cursors that use the ODBC Cursor Library. This library is intended for use with ODBC drivers that support only the default settings for cursor characteristics. Because both DB-Library and the SQL Server ODBC driver offer full support for cursor operations through server cursors, you should limit the use of client cursors.
You should use client cursors only to alleviate the restriction that server cursors do not support all Transact-SQL statements or batches. If a static, scrolling cursor is needed on a Transact-SQL statement or batch that cannot be executed with a server cursor, consider using a client cursor.
The operation to retrieve a row from a cursor is called a fetch. When working with Transact-SQL cursors, you can use FETCH statements to retrieve rows from a cursor's result set.
A FETCH statement supports a number of options that enable you to retrieve specific rows:
NOTE
When a cursor is opened, the current row position in the cursor is logically before the first row.
Transact-SQL cursors are limited to fetching one row at a time. API server cursors support fetching blocks of rows with each fetch. A cursor that supports fetching multiple rows at a time is called a block cursor. For more information about retrieving data from a cursor, refer to SQL Server Books Online.
There are two models for specifying the behavior of a cursor:
ADO and DB-Library support specifying only cursor types, not cursor behaviors. ODBC supports specifying cursor behavior by using either the cursor types or the cursor behaviors of scrollability and insensitivity.
Prior to SQL Server 7.0, the DECLARE CURSOR statement used to define Transact-SQL cursors supported only cursor behaviors of SCROLL and INSENSITIVE. In SQL Server 7.0, DECLARE CURSOR was extended to support cursor-type keywords.
OLE DB's cursor behavior model differs from both cursor behaviors and cursor types.
Do not specify both cursor types and cursor behaviors for a cursor. Use one or the other. Because ODBC and Transact-SQL cursors support both cursor behaviors and cursor types, use either ODBC or Transact-SQL when you are defining the cursor. The ODBC specification states that specifying both cursor behaviors and cursor types can lead to unpredictable results.
In SQL Server, the SELECT statement in a cursor definition is subject to the same transaction locking rules that apply to any other SELECT statement. In cursors, however, an additional set of scroll locks can be acquired based on the specification of a cursor concurrency level.
The transaction locks acquired by any SELECT statement, including the SELECT statement in a cursor definition, are controlled by the following options:
These locks are held until the end of the current transaction for both cursors and independent SELECT statements. When SQL Server is running in autocommit mode, each individual SQL statement is a transaction, and the locks are freed when the statement finishes. If SQL Server is running in explicit or implicit transaction mode, then the locks are held until the transaction is either committed or rolled back.
Transactions and locking are discussed in more detail in Chapter 12, "Managing SQL Server Transactions and Locks."
In this exercise, you will declare a Transact-SQL cursor, populate that cursor, retrieve rows from the cursor's result set, close the cursor, and deallocate the cursor. To complete this exercise, you should be logged into your Windows 2000 Server computer as Administrator.
USE BookShopDB DECLARE CustomerCrs CURSOR FOR SELECT * FROM Customers WHERE City = 'Seattle' ORDER BY LastName, FirstName
In this statement, you are declaring a Transact-SQL cursor named CustomerCrs. The cursor is associated with a SELECT statement that retrieves all the customers who live in Seattle from the Customers table.
A message appears in the Messages tab of the Results pane, stating that the command has been successfully completed.
OPEN CustomerCrs
In this statement, you are populating the cursor with the result from the SELECT statement that you specified when you declared the cursor.
A message appears in the Messages tab of the Results pane, stating that the command has been successfully completed.
FETCH NEXT FROM CustomerCrs
In this statement, you are retrieving the next row from the result set. Because this is the first row that you've retrieved from this cursor in this session, the row returned will be the first row in the result set.
The first row of the result set appears in the Grids tab of the Results pane.
The second row of the result set appears in the Grids tab of the Results pane.
CLOSE CustomerCrs
A message appears in the Messages tab of the Results pane, stating that the command has been successfully completed. The cursor has now been closed.
DEALLOCATE CustomerCrs
A message appears in the Messages tab of the Results pane, stating that the command has been successfully completed. The cursor has now been deallocated.
A cursor is an entity that maps over a result set and establishes a position on a single row within the result set. Cursors extend result-set processing. SQL Server supports three types of cursor implementations: Transact-SQL server cursors, API server cursors, and client cursors. Transact-SQL Server cursors are based on the DECLARE CURSOR statement and are used mainly in Transact-SQL scripts, stored procedures, and triggers. API server cursors support the API cursor functions in OLE DB, ODBC, and DB-Library. Like Transact-SQL cursors, API server cursors are implemented on the server. Client cursors are implemented internally by the SQL Server ODBC driver, the DB-Library DLL, and by the DLL that implements the ADO API. The operation to retrieve a row from a cursor is called a fetch. When working with Transact-SQL cursors, you can use FETCH statements to retrieve rows from a cursor's result set. There are two models for specifying the behavior of a cursor: cursor types and cursor behaviors. In SQL Server, the SELECT statement in a cursor definition is subject to the same transaction locking rules that apply to any other SELECT statement. In cursors, however, an additional set of scroll locks can be acquired based on the specification of a cursor concurrency level.