Using the USE PLAN Query Hint on Queries with Cursors

You can use the USE PLAN query hint with queries that specify cursor requests. The following table shows the cursor scroll options combinations that are supported with USE PLAN for API server cursors, Transact-SQL cursors that use Transact-SQL Extended Syntax, and Transact-SQL cursors that use ISO Syntax.

Scroll option (@scrollopt value for API server cursors)

USE PLAN supported for API server cursors

USE PLAN supported for Transact-SQL cursors that use Transact-SQL Extended Syntax

USE PLAN supported for Transact-SQL cursors that use ISO Syntax

STATIC

Y

Y

Not applicable

DYNAMIC

N

N

Not applicable

KEYSET

N

N

Not applicable

FORWARD_ONLY

N

N

Not applicable

FAST_FORWARD

Y

Y

Not applicable

FORWARD_ONLY STATIC

Not applicable

Y

Not applicable

INSENSITIVE

Not applicable

Not applicable

Y

Queries with cursors have two query plans associated with them, instead of the single plan associated with queries submitted without cursors. These plans may be of type OPEN, FETCH or REFRESH, depending on the type of the cursor.

One of the two plans for a cursor is directly generated from the input query, and the other plan is automatically generated. These plans are called the input query plan and the generated plan, respectively. The following table shows the plans that are generated for FAST_FORWARD and STATIC (INSENSITIVE) cursors.

Cursor type

Open cursor plan

Fetch cursor plan

Refresh cursor plan

FAST_FORWARD

Not applicable

Input query

Generated

STATIC

Input query

Generated

Not applicable

The XML query plans for a cursor query sometimes appear as a single XML document that contains both plans. These plans are called two-part plans.

The plans for a cursor also sometimes appear as two separate plans. For example, in a SQL Server Profiler trace for a STATIC API or Transact-SQL cursor query plan, you can see that two different Showplan XML For Query Compile events are generated. Only the input query (OPEN) plan is significant for plan forcing in this case. You should use the input query plan in a USE PLAN hint. A simple generated (FETCH) plan is also created but it is not required, or allowed, for plan forcing. You can recognize the input query (OPEN) plan because it is the plan that first gathers the set of rows that match the cursor query.

Important

Do not try to force a noncursor plan for a cursor query or vice versa. Plan forcing might fail if you do this, even if the cursor query and noncursor query are the same.

The following types of XML query plan outputs that describe cursor plans can be used to force a plan with USE PLAN for specific cursor types:

  • A two-part plan for the cursor

  • A one-part input query plan for the cursor

The cursor plan you force can be a plan obtained through any one of the following mechanisms for obtaining an XML query plan:

  • XML-based SQL Server Profiler trace events. These events can include Showplan XML, Showplan XML For Query Compile, and Showplan XML Statistics Profile.

  • SET SHOWPLAN_XML ON

  • SET STATISTICS XML ON

  • Dynamic management views and functions, such as the following query:

    SELECT *
    FROM sys.dm_exec_query_stats 
    CROSS APPLY sys.dm_exec_query_plan(plan_handle)
    

Observing API Server Cursor Usage by Applications

DB Library, ODBC, ADO, and OLEDB applications frequently interact with SQL Server by using API server cursors. You can see the calls that are submitted to API server cursor stored procedures by examining the SQL Server Profiler RPC:Starting events when an application that is built by using one of these interfaces is running.

Example: Forcing a Plan on a Query with a Cursor

This example assumes that you are using an application that interacts with the AdventureWorks2008R2 database by using ODBC cursors, and you want to force the plan for a query submitted to SQL Server by using an API server cursor routine. To force the plan, collect a plan for a query submitted through a cursor API routine and then create a plan guide to force the plan for that query. Have the application run the query again and examine the plan to verify that it has been forced.

Step 1: Collect the Plan

Start a SQL Server Profiler trace and select the Showplan XML and RPC:Starting events. Have the application execute the query for which you want to force the plan. Click the RPC:Starting event that is generated. Suppose the RPC:Starting event has the following text data:

DECLARE @p1 int
SET @p1=-1
DECLARE @p2 int
SET @p2=0
DECLARE @p5 int
SET @p5=8
DECLARE @p6 int
SET @p6=8193
DECLARE @p7 int
SET @p7=0
EXEC sp_cursorprepexec @p1 OUTPUT,@p2 OUTPUT,NULL,N'SELECT * FROM Sales.SalesOrderHeader h, Sales.SalesOrderDetail d WHERE h.SalesOrderID = d.SalesOrderID AND h.OrderDate BETWEEN ''20030101'' AND ''20040101''',@p5 OUTPUT,@p6 OUTPUT,@p7 OUTPUT
SELECT @p1, @p2, @p5, @p6, @p7

Collect the plan for the query by right clicking the Showplan XML trace event containing the input query plan for the query that appears as an argument to the sp_cursorprepexec statement above, and then selecting Extract Event Data. Save the event data (an XML showplan) to a file CursorPlan.SQLPlan on the desktop. Copy the file CursorPlan.SQLPlan to CursorPlan.txt. In SQL Server Management Studio, open CursorPlan.txt in an editor window. To save time later, use Find and Replace to replace each single quotation mark (') in the plan with four single quotation marks (''''). Save CursorPlan.txt.

Step 2: Create the Plan Guide to Force the Plan

Create a plan guide by writing and executing the following sp_create_plan_guide statement to force the plan. This plan guide definition includes the XML plan captured in the previous step in a USE PLAN query hint in the plan guide.

When you are writing this plan guide definition, paste the contents of CursorPlan.txt into the appropriate position in the @hints argument (just after OPTION(USE PLAN N'').

exec sp_create_plan_guide 
@name = N'CursorGuide1',
@stmt = N'SELECT * FROM Sales.SalesOrderHeader h, Sales.SalesOrderDetail d WHERE h.SalesOrderID = d.SalesOrderID AND h.OrderDate BETWEEN ''20030101'' AND ''20040101''',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION(USE PLAN N''<ShowPlanXML xmlns=''''https://schemas.microsoft.com/sqlserver/2004/07/showplan'''' Version=''''0.5'''' Build=''''9.00.1116''''><BatchSequence><Batch><Statements><StmtSimple>
   …
</StmtSimple></Statements></Batch></BatchSequence></ShowPlanXML>'')'

Step 3: Execute the Query and Verify That the Plan Guide Is Applied to It

Have the application run the query again and gather its XML execution plan by using the XML Showplan event in SQL Server Profiler.

Click the XML Showplan event for the plan. You should see that the plan is the one that you forced in the plan guide.

Parameterized Cursor Queries

If the API server cursor query that you want to create a plan guide for is parameterized, make sure you include both the statement string and the parameter definition string you see in the SQL Server Profiler RPC:Starting event in the plan guide definition. The parameter definition string is also required to obtain a successful plan guide match, just as it is with parameterized queries submitted by using sp_executesql.