TuningSybase 1
TuningSybase 1
To order additional documents, U.S. and Canadian customers should call Customer Fulfillment at (800) 685-8225, fax (617) 229-9845.
Customers in other countries with a U.S. license agreement may contact Customer Fulfillment via the above fax number. All other
international customers should contact their Sybase subsidiary or local distributor. Upgrades are provided only at regularly scheduled
software release dates. No part of this publication may be reproduced, transmitted, or translated in any form or by any means, electronic,
mechanical, manual, optical, or otherwise, without the prior written permission of Sybase, Inc.
Sybase trademarks can be viewed at the Sybase trademarks page at http://www.sybase.com/detail?id=1011207. Sybase and the marks listed
are trademarks of Sybase, Inc. ® indicates registration in the United States of America.
SAP and other SAP products and services mentioned herein as well as their respective logos are trademarks or registered trademarks of
SAP AG in Germany and in several other countries all over the world.
Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
Unicode and the Unicode Logo are registered trademarks of Unicode, Inc.
IBM and Tivoli are registered trademarks of International Business Machines Corporation in the United States, other countries, or both.
All other company and product names mentioned may be trademarks of the respective companies with which they are associated.
Use, duplication, or disclosure by the government is subject to the restrictions set forth in subparagraph (c)(1)(ii) of DFARS 52.227-7013
for the DOD and as set forth in FAR 52.227-19(a)-(d) for civilian agencies.
Performance and Tuning Series: Query Processing and Abstract Plans iii
Contents
Performance and Tuning Series: Query Processing and Abstract Plans vii
When additional statistics may be useful............................... 303
Adding statistics for a column with update statistics.............. 305
Adding statistics for minor columns with update index statistics ..
305
Adding statistics for all columns with update all statistics...... 306
Choosing step numbers for histograms........................................ 306
Choosing a step number........................................................ 307
Scan types, sort requirements, and locking.................................. 307
Sorts for unindexed or nonleading columns .......................... 308
Locking, scans, and sorts during update index statistics....... 308
Locking, scans and sorts during update all statistics............. 309
Using the with consumers clause .......................................... 309
Reducing the impact of update statistics on concurrent processes
309
Using the delete statistics command ............................................ 310
When row counts may be inaccurate ........................................... 311
Index............................................................................................................................................ 387
1: Parser
2:Normalization
3: Preprocessor
4: Optimizer
5: Code generator
Query optimizer
The query optimizer provides speed and efficiency for online transaction
processing (OLTP) and operational decision-support systems (DSS)
environments. You can choose an optimization strategy that best suits your
query environment.
The query optimizer is self-tuning, and requires fewer interventions than
versions of Adaptive Server Enterprise ealier than 15.0. It relies infrequently
on worktables for materialization between steps of operations; however, the
query optimizer may use more worktables when it determines that hash and
merge operations are more effective.
Some of the key features in the release 15.0 query optimizer include support
for:
• New optimization techniques and query execution operator supports that
enhance query performance, such as:
• On-the-fly grouping and ordering operator support using in-memory
sorting and hashing for queries with group by and order by clauses
• hash and merge join operator support for efficient join operations
• index union and index intersection strategies for queries with predicates
on different indexes
Table 1-1 on page 4 is a list of optimization techniques and operator
support provided in Adaptive Server Enterprise. Many of these techniques
map directly to the operators supported in the query execution. See “Lava
query execution engine” on page 20.
• Improved index selection, especially for joins with or clauses, and joins
with and search arguments (SARGs) with mismatched but compatible
datatypes
• Improved costing that employs join histograms to prevent inaccuracies that
might otherwise arise due to data skews in joining columns
• New cost-based pruning and timeout mechanisms in join ordering and plan
strategies for large, multiway joins, and for star and snowflake schema
joins
• New optimization techniques to support data and index partitioning
(building blocks for parallelism) that are especially beneficial for very
large data sets
• Improved query optimization techniques for vertical and horizontal
parallelism. See Chapter 5, “Parallel Query Processing.”
operator Description
group hashing Determines whether the query optimizer may use a group hashing algorithm to process
aggregates.
Technique Description
multi table store ind Determines whether the query optimizer may use reformatting on the result of a
multiple table join. Using multi table store ind may increase the use of worktables.
opportunistic distinct view Determines whether the query optimizer may use a more flexible algorithm when
enforcing distinctness.
index intersection Determines whether the query optimizer may use the intersection of multiple index
scans as part of the query plan in the search space.
select *
from t1, t2, t3
where t1.c11 = t2.c21
and t2.c21 = t3.c31
and t3.c31 = 1
Without join transitive closure, the only join orders considered are (t1, t2, t3),
(t2, t1, t3), (t2, t3, t1),and (t3, t2, t1). By adding the join on t1.c11 = t3.31, the
query processor expands the list of join orders to include: (t1, t3, t2) and (t3, t1,
t2). Search argument transitive closure applies the condition specified by t3.c31
= 1 to the join columns of t1 and t2.
• Outer joins; for example, t1.c11 *= t2.c2, or left join or right join
• Joins across subquery boundaries
• Joins used to check referential integrity or the with check option on views
You can use search arguments to determine the access path to the data rows
when a column in the where clause matches an index key. The index can be
used to locate and retrieve the matching data rows. Once the row has been
located in the data cache or has been read into the data cache from disk, any
remaining clauses are applied.
For example, if the authors table has on an index on au_lname and another on
city, either index can be used to locate the matching rows for this query:
select au_lname, city, state
from authors
where city = “Washington"
and au_lname = “Catmull"
The query optimizer uses statistics, including histograms, the number of rows
in the table, the index heights, and the cluster ratios for the index and data pages
to determine which index provides the cheapest access. The index that provides
the cheapest access to the data pages is chosen and used to execute the query,
and the other clause is applied to the data rows once they have been accessed.
Nonequality operators
The non-equality operators, < > and !=, are special cases. The query optimizer
checks whether it should cover nonclustered indexes if the column is indexed,
and uses a nonmatching index scan if an index covers the query. However, if
the index does not cover the query, the table is accessed through a row ID
lookup of the data pages during the index scan.
column name */
These two clauses can be optimized if written in this form:
advance = 5000/2
au_lname like "Ben%"
Consider this query, with the only index on au_lname:
select au_lname, au_fname, phone
from authors
where au_lname = “Gerland”
and city = "San Francisco"
The clause qualifies as a search argument:
au_lname = “Gerland"
• There is an index on au_lname
• There are no functions or other operations on the column name.
• The operator is a valid search argument operator.
This clause matches all the criteria above except the first; there is no index on
the city column. In this case, the index on au_lname is used for the query. All
data pages with a matching last name are brought into cache, and each
matching row is examined to see if the city matches the search criteria.
Handling joins
The query optimizer processes join predicates the same way it processes search
arguments, in that it uses statistics, number of rows in the table, index heights,
and the cluster ratios for the index and data pages to determine which index and
join method provides the cheapest access. In addition, the query optimizer also
uses join density estimates derived from join histograms that give accurate
estimates of qualifying joining rows and the rows to be scanned in the outer and
inner tables. The query optimizer also must decide on the optimal join ordering
that will yield the most efficient query plan. The next sections describe the key
techniques used in processing joins.
The join density is dynamically computed from the “join histogram,” which
considers the joining of histograms from both sides of the join operator. The
first histogram join occurs typically between two base tables when both
attributes have histograms. Every histogram join creates a new histogram on
the corresponding attribute of the parent join's projection.
The outcome of the join histogram technique is accurate join selectivity
estimates, even if data distributions of the joining columns are skewed,
resulting in superior join orders and performance.
As long as datatypes are implicitly convertible, the query optimizer can use
index scans to process the join. In other words, the query optimizer uses the
column value from the outer table to position the index scan on the inner table,
even when the lookup value from the outer table has a different datatype than
the respective index attribute of the inner table.
join ordering
One of the key tasks of the query optimizer is to generate a query plan for join
queries so that the order of the relations in the joins processed during query
execution is optimal. This involves elaborate plan search strategies that can
consume significant time and memory. The query optimizer uses several
effective techniques to obtain the optimal join ordering. The key techniques
are:
• Use of a greedy strategy to obtain an initial good ordering that can be used
as an upper boundary to prune out other, subsequent join orderings. The
greedy strategy employs join row estimates and the nested-loop-join
method to arrive at the initial ordering.
• An exhaustive ordering strategy follows the greedy strategy. In this
strategy, a potentially better join ordering replaces the join ordering
obtained in the greedy strategy. This ordering may employ any join
method.
• Use of extensive cost-based and rule-based pruning techniques eliminates
undesirable join orders from consideration. The key aspect of the pruning
technique is that it always compares partial join orders (the prefix of a
potential join ordering) against the best complete join ordering to decide
whether to proceed with the given prefix. This significantly improves the
time required determine an optimal join order.
• The query optimizer can recognize and process star or snowflake schema
joins and process their join ordering in the most efficient way. A typical
star schema join involves a large Fact table that has equi-join predicates that
join it with several Dimension tables. The Dimension tables have no join
predicates connecting each other; that is, there are no joins between the
Dimension tables themselves, but there are join predicates between the
Dimension tables and the Fact table. The query optimizer employs special
join ordering techniques during which the large Fact table is pushed to the
end of the join order and the Dimension tables are pulled up front, yielding
highly efficient query plans. The query optimizer does not, however, use
this technique if the star schema joins contain subqueries, outer joins, or or
predicates.
Optimization goals
Optimization goals are a convenient way to match query demands with the best
optimization techniques, thus ensuring optimal use of the optimizer’s time and
resources. The query optimizer allows you to configure three types of
optimization goals, which you can specify at three tiers: server level, session
level, and query level.
Set the optimization goal at the desired level. The server-level optimization
goal is overridden at the session level, which is overridden at the query level.
These optimization goals allow you to choose an optimization strategy that best
fits your query environment:
• allrows_mix – the default goal, and the most useful goal in a mixed-query
environment. allows_mix balances the needs of OLTP and DSS query
environments.
• allrows_dss – the most useful goal for operational DSS queries of medium
to high complexity. Currently, this goal is provided on an experimental
basis.
• allrows_oltp – the optimizer considers only nested-loop joins.
At the query level, use a select or other DML command. For example:
select * from A order by A.a plan
"(use optgoal allrows_dss)"
In general, you can set query-level optimization goals using select, update, and
delete statements. However, you cannot set query-level optimization goals in
pure insert statements, although you can set optimization goals in insert…select
statements.
A small timeout value may be used when you want a faster compilation time
from complex ad hoc queries that normally take a long time to compile.
However, for most queries, the default timeout value of 10 should suffice.
Use sp_configure to set the optimization timeout limit configuration parameter
at the server level. For example, to limit optimization time to 10% of total
query processing time, enter:
sp_configure “optimization timeout limit", 10
Use set to set timeout at the session level:
set plan opttimeoutlimit <n>
where n is any integer between 0 and 4000.
Use select to limit optimization time at the query level:
select * from <table> plan "(use opttimeoutlimit <n>)"
where n is any integer between 0 and 1000.
Parallelism
Adaptive Server supports horizontal and vertical parallelism for query
execution. Vertical parallelism is the ability to run multiple operators at the
same time by employing different system resources such as CPUs, disks, and
so on. Horizontal parallelism is the ability to run multiple instances of an
operator on the specified portion of the data.
See Chapter 5, “Parallel Query Processing,” for a more detailed discussion of
parallel query optimization in Adaptive Server.
Optimization issues
Although the query optimizer can efficiently optimize most queries, these
issues may effect the optimizer’s efficiency:
• If statistics have not been updated recently, the actual data distribution
may not match the values used to optimize queries.
• The rows referenced by a specified transaction may not fit the pattern
reflected by the index statistics.
• An index may access a large portion of the table.
• where clauses (SARGS) are written in a form that cannot be optimized.
• If an index is not being used when you expect it to be, use output from the
set commands in Table 3-1 on page 112 to see whether the query
processor is considering the index.
Use of SQL derived Queries expressed as a single SQL statement exploit the query processor better
tables than queries expressed in two or more SQL statements. SQL-derived tables
enable you to express, in a single step, what might otherwise require several
SQL statements and temporary tables, especially where intermediate aggregate
results must be stored. For example:
select dt_1.* from
(select sum(total_sales)
from titles_west group by total_sales)
dt_1(sales_sum),
(select sum(total_sales)
from titles_east group by total_sales)
dt_2(sales_sum)
where dt_1.sales_sum = dt_2.sales_sum
Here, aggregate results are obtained from the SQL derived tables dt_1 and dt_2,
and a join is computed between the two SQL derived tables. Everything is
accomplished in a single SQL statement.
For more information, see Chapter 9, “SQL Derived Tables,” in the Transact-
SQL User's Guide.
Tuning according to To understand query and system behavior, know the sizes of your tables and
object sizes indexes. At several stages of tuning work, you need size data to:
• Understand statistics i/o reports for a specific query plan.
• Understand the query processor's choice of query plan. The Adaptive
Server cost-based query processor estimates the physical and logical I/O
required for each possible access method and selects the cheapest method.
• Determine object placement, based on the sizes of database objects and on
the expected I/O patterns on the objects.
To improve performance, distribute database objects across physical
devices, so that reads and writes to disk are evenly distributed.
Object placement is described in the Performance and Tuning Series:
Physical Database Tuning.
• Understand changes in performance. If objects grow, their performance
characteristics can change. For example, consider a table that is heavily
used and is usually 100 percent cached. If the table grows too large for its
cache, queries that access the table can suffer poor performance. This is
particularly true of joins that require multiple scans.
The Lava query execution engine executes Lava query plans. All query plans
chosen by the optimizer are compiled into Lava query plans. However, SQL
statements that are not optimized, such as set or create, are compiled into query
plans like those in versions of Adaptive Server earlier than 15.0, and are not
executed by the Lava query execution engine. Non-Lava query plans are either
executed by the procedural execution engine or by utility modules called by the
procedural engine. Adaptive Server version 15.0 and later has two distinct
kinds of query plans and this is clearly seen in the showplan output (see
Chapter 3, “Displaying Query Optimization Strategies and Estimates”).
Emit
NestedLoopJoin
IndexScan IndexScan
sysobjects(o) syscolumns(c)
The Lava query plan for this query consists of four Lava operators. The top
operator is an Emit (also called Root) operator that dispatches the results of
query execution either by sending the rows to the client or by assigning values
to local variables.
The only child operator of the Emit is a NestedLoopJoin (NL Join)that uses the
nested loop join algorithm to join the rows coming from its two child operators,
(1) the Scan of sysobjects and (2) the scan of syscolumns.
Since the optimizer optimizes all select, insert, delete and update statements,
these are always compiled into Lava query plans and executed by the Lava
query engine.
Some SQL statements are compiled into hybrid query plans. Such plans have
multiple steps, some of which are executed by the Utility modules, and a final
step that is a Lava query plan. An example is the select into statement; select
into is compiled into a two-step query plan:
• A Lava query plan to inserts the rows into the target table. To execute this
query plan, the Procedural Execution Engine calls the create table utility
to execute the first step to create the table.
Then the procedural engine calls the Lava query execution engine to execute
the Lava query plan to select and insert the rows into the target table.
The two other SQL statements that generate hybrid query plans are alter table
(but only when data copying is required) and reorg rebuild.
A Lava query plan is also generated and executed to support bcp. The support
for bcp in Adaptive Server has always been in the bcp utility. Now, in version
15.0 and later, the bcp utility generates a Lava query plan and calls the Lava
query execution engine to execute the plan.
See Chapter 2, “Using showplan” for more examples of Lava query plans.
Lava operators
Lava query plans are built of Lava operators. Each Lava operator is a self-
contained software object that implements one of the basic physical operations
that the optimizer uses to build query plans. Each Lava operator has five
methods that can be called by its parent operator. These five methods
correspond to the five phases of query execution:
• Acquire
• Open
• Next
• Close,
• Release
Because the Lava operators all provide the same methods (that is, the same
API), they can be interchanged like building blocks in a Lava query plan. For
example, you can replace the NLJoin operator in Figure 1-2 with a MergeJoin
operator or a HashJoin operator without impacting any of the other three
operators in the query plan.
The Lava operators that can be chosen by the optimizer to build Lava query
plans are listed in Table 1-3:
Table 1-3: Lava operators
Operator Description
BulkOp Executes the part of bcp processing that is done in the Lava query engine. Only found
in query plans that are created by the bcp utility, not those created by the optimizer.
CacheScanOp Reads rows from an in-memory table.
DelTextOp Deletes text page chains as part of alter table drop column processing.
DeleteOp Deletes rows from a local table.
Deletes rows from a proxy table when the entire SQL statement cannot be shipped to
the remote server. See also RemoteScanOp.
Operator Description
EmitOp (RootOp) Routes query execution result rows. Can send results to the client or assign result values
to local variables or fetch into variables. An EmitOp is always the top operator in a Lava
query plan.
EmitExchangeOp Routes result rows from a subplan that is executed in parallel to the ExchangeOp in the
parent plan fragment. EmitExchangeOp always appears directly under an ExchangeOp.
See Chapter 5, “Parallel Query Processing.”
GroupSortedOp Performs vector aggregation (group by) when the input rows are already sorted on the
(Aggregation) group-by columns. See also HashVectorAggOp.
GroupSorted (Distinct) Eliminates duplicate rows. Requires the input rows to be sorted on all columns. See also
HashDistinctOp and SortOp (Distinct).
HashVectorAggOp Performs vector aggregation (group by). Uses a Hash algorithm to group the input rows,
so no requirements on ordering of the input rows. See also GroupSortedOp
(Aggregation).
HashDistinctOp Eliminates duplicate rows using a hashing algorithm to find duplicate rows. See also
GroupSortedOp (Distinct) and SortOp (Distinct).
HashJoinOp Performs a join of two input row streams using the HashJoin algorithm.
HashUnionOp Performs a union operation of two or more input row streams using a hashing algorithm
to find and eliminate duplicate rows. See also MergeUnionOp and UnionAllOp.
InsScrollOp Implements extra processing needed to support insensitive scrollable cursors. See also
SemiInsScrollOp.
InsertOp Inserts rows to a local table.
Inserts rows to a proxy table when the entire SQL statement cannot be shipped to the
remote server. See also RemoteScanOp.
MergeJoinOp Performs a join of two streams of rows that are sorted on the joining columns using the
mergejoin algorithm.
MergeUnionOp Performs a union or union all operation on two or more sorted input streams. Guarantees
that the output stream retains the ordering of the input streams. See also HashUnionOp
and UnionAllOp.
NestedLoopJoinOp Performs a join of two input streams using the NestedLoopJoin algorithm.
NaryNestedLoopJoinOp Performs a join of three or more input streams using an enhanced NestedLoopJoin
algorithm. This operator replaces a left-deep tree of NestedLoopJoin operators and can
lead to significant performance improvements when rows of some of the input streams
can be skipped.
OrScanOp Inserts the in or or values into an in-memory table, sorts the values, and removes the
duplicates.Then returns the values, one at a time. Only used for SQL statements with in
clauses or multiple or clauses on the same column.
PtnScanOp Reads rows from a local table (partitioned or not) using either a table scan or an index
scan to access the rows.
RIDJoinOp Receives one or more row identifiers (RIDs) from its left-child operator and calls on its
right-child operator (PtnScanOp) to find the corresponding rows. Used only on SQL
statements with or clauses on different columns of the same table.
Operator Description
RIFilterOp (Direct) Drives the execution of a subplan to enforce referential integrity constraints that can be
checked on a row-by-row basis.
Appears only in insert, delete, or update queries on tables with referential integrity
constraints.
RIFilterOp (Deferred) Drives the execution of a sub-plan to enforce referential integrity constraints that can
only be checked after all rows that are affected by the query have been processed.
RemoteScanOp Accesses proxy tables. The RemoteScanOp can:
• Read rows from a single proxy table for further processing in a Lava query plan on
the local host.
• Pass complete SQL statements to a remote host for execution: insert, delete, update,
and select statements. In this case, the Lava query plan consists of an EmitOp with a
RemoteScanOp as its only child operator.
• Pass an arbitrarily complex query plan fragment to a remote host for execution and
read in the result rows (function shipping).
RestrictOp Evaluates expressions.
SQFilterOp Drives the execution of a subplan to execute one or more subqueries.
ScalarAggOp Performs scalar aggregation, such as aggregates without group by.
SemiInsScrollOp Performs extra processing to support semi-insensitive scrollable cursors. See also
InsScrollOp.
SequencerOp Enforces sequential execution of different sub-plans in the query plan.
SortOp Sorts its input rows based upon specified keys.
SortOp (Distinct) Sorts its input and removes duplicate rows. See also HashDisitnctOp and
GroupSortedOp (Distinct).
StoreOp Creates and coordinates the filling of a worktable, and creates a clustered index on the
worktable if required. StoreOp can only have an InsertOp as a child; the InsertOp
populates the worktable.
UnionAllOp Performs a union all operation on two or more input streams. See also HashUnionOp and
MergeUnionOp.
UpdateOp Changes the value of columns in rows of a local table or of a proxy table when the entire
update statement cannot be sent to the remote server. See also RemoteScanOp.
ExchangeOp Enables and coordinates parallel execution of Lava query plans. The ExchangeOp can
be inserted between almost any two Lava operators in a query plan to divide the plan
into sub-plans that can be executed in parallel. See Chapter 5, “Parallel Query
Processing.”
• Close phase
After all rows have been returned, the Close method of the Emit operator
is invoked, which in turn calls Close of the NLJoin operator, which in turn
calls Close on both of its child operators.
• Release phase
The Release method of the Emit operator is invoked and the calls to the
Release method of the other operators is propagated down the query plan.
After successfully completing the Release phase of execution, the Lava query
engine returns control to the Procedural Execution Engine for final statement
processing.
Direct updates
Adaptive Server performs direct updates in a single pass:
• It locates the affected index and data rows.
• It writes the log records for the changes to the transaction log.
• It makes the changes to the data pages and any affected index pages.
There are three techniques for performing direct updates:
• In-place updates
• Cheap direct updates
• Expensive direct updates
Direct updates require less overhead than deferred updates and are generally
faster, as they limit the number of log scans, reduce logging, save traversal of
index B-trees (reducing lock contention), and save I/O because Adaptive
Server does not have to refetch pages to perform modifications based on log
records.
In-place updates
Adaptive Server performs in-place updates whenever possible.
Deferred updates
Adaptive Server uses deferred updates when direct update conditions are not
met. A deferred update is the slowest type of update.
In a deferred update, Adaptive Server:
• Locates the affected data rows, writing the log records for deferred delete
and insert of the data pages as rows are located.
• Reads the log records for the transaction and performs the deletes on the
data pages and any affected index rows.
• Reads the log records a second time, and performs all inserts on the data
pages, and inserts any affected index rows.
Pointer
Page 1242
Key
Before update: 10 O’Leary
11 Ringer
Page 1132
Pointer
Row ID
12 White
Row ID
Pointer
Chan 1129,3
Dull 1409,1 Page 1307
Page 1001 Page 1007 Edwards 1018,5 14 Hunter
Bennet 1421,1 Bennet 1421,1 15 Smith
1007 1132 16 Ringer
Greane 1307,4 Page 1133
Karsen 1411,3 17 Greane
1133 Greane 1307,4
1009
Green 1421,2
Page 1421
Greene 1409,2
Page 1009 18 Bennet
Karsen 1411,3 19 Green
1315 Page 1127 20
Hunter 1307,1 Yokomoto
Jenkins 1242,4 Page 1409
21 Dull
22 Greene
23 White
Page 1421
Step 2: Change 18 Bennet
19
data page.
Hubbard
20
Optimizing updates
showplan messages provide information about whether an update is performed
in direct mode or deferred mode. If a direct update is not possible, Adaptive
Server updates the data row in deferred mode. There are times when the
optimizer cannot know whether a direct update or a deferred update will be
performed, so two showplan messages are provided:
• The “deferred_varcol” message shows that the update may change the
length of the row because a variable-length column is being updated. If the
updated row fits on the page, the update is performed in direct mode; if the
update does not fit on the page, the update is performed in deferred mode.
• The “deferred_index” message indicates that the changes to the data pages
and the deletes to the index pages are performed in direct mode, but the
inserts to the index pages are performed in deferred mode.
These types of direct updates depend on information that is available only at
runtime, since the page actually has to be fetched and examined to determine
whether the row fits on the page.
If the key for an index is fixed length, the only difference in update modes from
those shown in the table occurs for nonclustered indexes. For a nonclustered,
nonunique index, the update mode is deferred_index for updates to the key. For
a nonclustered, unique index, the update mode is direct for updates to the key.
If the length of varchar or varbinary is close to the maximum length, use char or
binary instead. Each variable-length column adds row overhead and increases
the possibility of deferred updates.
Using max_rows_per_page to reduce the number of rows allowed on a page
increases direct updates, because an update that increases the length of a
variable-length column may still fit on the same page.
For more information on using max_rows_per_page, see the Performance and
Tuning Series: Physical Database Tuning.
This chapter describes the messages printed by the showplan utility, which
displays the query plan in a text-based format for each SQL statement in
a batch or stored procedure.
Topic Page
Displaying a query plan 37
Statement-level output 44
Query plan shape 47
Union operators 90
INSTEAD-OF TRIGGER operators 106
• allrows_mix – the query processor allows both nested-loop joins and merge
joins. The query processor measures their relative costs to determine
which join it uses.
• allrows_dss – the query processor uses nested-loop, merge-, or hash-joins.
The query processor measures their relative costs to determine which join
it uses.
STEP 1
The type of query is EXECUTE.
QUERY PLAN FOR STATEMENT 1 (at line 4).
STEP 1
end
go
create procedure sp_A
as
begin
select * from titles
execute sp_B
end
go
set showplan on
go
set fmtonly on
go
STEP 1
The type of query is SET OPTION ON.
sp_B
go
STEP 1
The type of query is EXECUTE.
STEP 1
(0 rows affected)
(return status = 0)
sp_A
go
STEP 1
The type of query is EXECUTE.
STEP 1
STEP 1
The type of query is EXECUTE.
title_id
title
pubdate contract
--------
--------------------------------------------------------------------
----------- -------- ------------------- -----------------------
-----------
---------------------------------------------------------------------
-----------------------------------------------------------------------------
-------------------------------------------------
-------------------- --------
(0 rows affected)
STEP 1
Statement-level output
The first section of showplan output for each query plan presents statement-
level information, including the statement and line number in the batch or
stored procedure of the query for which the query plan was generated:
QUERY PLAN FOR STATEMENT N (at line N).
This message may be followed by a series of messages that apply to the
statement’s entire query plan. If the query plan was generated using an abstract
plan about how the abstract plan was forced:
• If an explicit abstract plan was given by a plan clause in the SQL
statement, the message is:
Optimized using the Abstract Plan in the PLAN clause.
• If an abstract plan has been internally generated (that is, for alter table and
reorg commands that are executed in parallel), the message is:
Optimized using the forced options (internally
generated Abstract Plan).
• If a new statement is cached, the output includes:
STEP 1
. . .
. . .
• If an abstract plan has been retrieved from sysqueryplans because
automatic abstract plan usage is enabled, the message is:
Note The VA= in the showplan output is available for Adaptive Server
version 15.0.2 ESD #2 and later. You will not see VA= in earlier versions
of Adaptive Server.
• Adaptive Server uses a scan descriptor for each database object that is
accessed during query execution. By default, each connection (or each
worker process for parallel query plans) has 28 scan descriptors. If the
query plan requires access to more than 28 database objects, auxiliary scan
descriptors are allocated from a global pool. If the query plan uses
auxiliary scan descriptors, this message is printed, showing the total
number required:
Auxiliary scan descriptors required: N
• This message shows the total number of operators appearing in the query
plan:
N operator(s) under root
• The next message shows the type of query for the query plan. For query
plans, the query type is select, insert, delete, or update:
The type of query is SELECT.
• A final statement-level message is printed at the end of showplan output if
Adaptive Server is configured to enable resource limits. The message
displays the optimizer’s total estimated cost of logical and physical I/O:
Total estimated I/O cost for statement N (at line M):
X.
The following query, with showplan output, shows some of these messages:
use pubs2
go
set showplan on
go
select stores.stor_name, sales.ord_num
from stores, sales, salesdetail
where salesdetail.stor_id = sales.stor_id
and stores.stor_id = sales.stor_id
plan " ( m_join ( i_scan salesdetailind salesdetail)
( m_join ( i_scan salesind sales ) ( sort ( t_scan stores ) ) ) )"
STEP 1
The type of query is SELECT.
| | | Forward Scan.
| | | Positioning at start of table.
| | | Using I/O Size 2 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for data pages.
| |
| | |SORT Operator (VA = 3)
| | | Using Worktable1 for internal storage.
| | |
| | | |SCAN Operator (VA = 2)
| | | | FROM TABLE
| | | | stores
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Using I/O Size 2 Kbytes for data pages.
| | | | With LRU Buffer Replacement Strategy for data pages.
After the statement level output, the query plan appears. The showplan output
of the query plan consists of two components:
• The names of the operators (some provide additional information) to show
which operations are being executed in the query plan.
• Vertical bars (the “|” symbol) with indentation to show the shape of the
query plan operator tree.
EMIT
VA=
SCAN SORT
sales VA=
VA=
SCAN
stores
VA=
To generate a result row, the EMIT operator calls for a row from its child, the
MERGE JOIN operator (1), which calls for a row from its left child, the SCAN
operator for salesdetailind. When EMIT receives a row from its left child,
MERGE JOIN operator (1) calls for a row from its right child, MERGE JOIN
operator (2). MERGE JOIN operator (2) calls for a row from its left child, the
SCAN operator for sales.
When it receives a row from its left child, MERGE JOIN operator (2) calls for
a row from its right child, the SCAN operator. The SCAN operator is a data-
blocking operator. That is, it needs all of its input rows before it can sort them,
so the SORT operator keeps calling for rows from its child, the SCAN operator
for stores, until all rows have been returned. Then the SORT operator sorts
the rows and passes the first row to the MERGE JOIN operator (2).
The MERGE JOIN operator (2) keeps calling for rows from either the left or
right child operators until it gets two rows that match on the joining keys. The
matching row is then passed up to MERGE JOIN operator (1). MERGE JOIN
operator (1) also calls for rows from its child operators until a match is found,
which is then passed up to the EMIT operator to be returned to the client. In
effect, the operators are processed using a left-deep postfix recursive strategy.
Figure 2-2 shows a graphical representation of an alternate query plan for the
same example query. This query plan contains all of the same operators, but the
shape of the tree is different.
Figure 2-2: Alternate query plan
EMIT
VA=
MergeJoinOp(1)
Inner join
VA=
MergeJoinOp(2) ScanOp
Inner join salesdetailind
VA= VA=
ScanOp SortOp
sales VA=
VA=
ScanOp
stores
VA=
The showplan output corresponding to the query plan in Figure 2-2 is:
QUERY PLAN FOR STATEMENT 1 (at line 1).
ROOT:EMIT Operator
The showplan output conveys the shape of the query plan by using indentation
and the pipe (“|”) symbol to indicate which operators are under which and
which ones are on the same or different branches of the tree. There are two
rules to interpreting the tree shape:
• The pipe “|” symbols form a vertical line that starts at the operator’s name
and continue down past all of the operators that are under it on the same
branch.
• Child operators are indented to the left for each level of nesting.
Using these rules, the shape of the query plan in Figure 2-2 can be derived from
the previous showplan output with the following steps:
1 The ROOT or EMIT operator is at the top of the query plan tree.
2 MERGE JOIN operator (1) is the left child of the ROOT. The vertical line
that starts at MERGE JOIN operator (1) travels down the length of the entire
output, so all of the other operators are below MERGE JOIN operator (1)
and on the same branch.
3 The left child operator of the MERGE JOIN operator (1) is MERGE JOIN
operator (2).
4 The vertical line that starts at MERGE JOIN operator (2) travels down past
a SCAN, a SORT, and another SCAN operator before it ends. These operators
are all nested as a subbranch under MERGE JOIN operator (2).
5 The first SCAN under MERGE JOIN operator (2) is its left child, the SCAN
of the sales table.
6 The SORT operator is the right child of MERGE JOIN operator (2) and the
SCAN of the stores table is the only child of the SORT operator.
7 Below the output for the SCAN of the stores table, several vertical lines
end. This indicates that a branch of the tree has ended.
8 The next output is for the SCAN of the salesdetail table. It has the same
indentation as MERGE JOIN operator (2), indicating that it is on the same
level. In fact, this SCAN is the right child of MERGE JOIN operator (1).
Note Most operators are either unary or binary. That is, they have either a
single child operator or two child operators directly beneath. Operators that
have more than two child operators are called “nary”. Operators that have no
children are leaf operators in the tree and are termed “nullary.”
Another way to get a graphical representation of the query plan is to use the
command set statistics plancost on. See Adaptive Server Reference Manual:
Commands for more information. This command is used to compare the
estimated and actual costs in a query plan. It prints its output as a semigraphical
tree representing the query plan tree. It is a very useful tool for diagnosing
query performance problems.
EMIT operator
The EMIT operator appears at the top of every query plan. EMIT is the root of
the query plan tree and always has exactly one child operator. The EMIT
operator routes the result rows of the query by sending them to the client (an
application or another Adaptive Server instance) or by assigning values from
the result row to local variables or fetch into variables.
SCAN operator
The SCAN operator reads rows into the query plan and makes them available
for further processing by the other operators in the query plan. The SCAN
operator is a leaf operator; that is, it never has any child operators. The SCAN
operator can read rows from multiple sources, so the showplan message
identifying it is always followed by a FROM message to identify what kind of
SCAN is being performed. The FROM messages are: FROM CACHE, FROM OR,
FROM LIST, and FROM TABLE.
FROM or LIST
An OR list has as many as N rows; one for each distinct OR or IN value specified
in the query.
The first message shows that an OR scan is reading rows from an in-memory
table that contains values from an IN list or multiple or clauses on the same
column. The OR list appears only in query plans that use the special or strategy
for in lists. The second message shows the maximum number of rows (N) that
the in-memory table can have. Since OR list eliminates duplicate values when
filling the in-memory table, N may be less than the number of values appearing
in the SQL statement. As an example, the following query generates a query
plan with the special or strategy and an OR list:
select s.id from sysobjects s where s.id in (1, 0, 1, 2, 3)
go
STEP 1
The type of query is SELECT.
This example has five values in the IN list, but only four are distinct, so the OR
list puts only the four distinct values in its in-memory table. In the example
query plan, the OR list is the left-child operator of the NESTED LOOP JOIN
operator and a SCAN operator is the right child of the NESTED LOOP JOIN
operator. When this plan executes, the NESTED LOOP JOIN operator calls the
or command to return a row from its in-memory table, then the NESTED LOOP
JOIN operator calls on the SCAN operator to find all matching rows (one at a
time), using the clustered index for lookup. This example query plan is much
more efficient than reading all of the rows of sysobjects and comparing the
value of sysobjects.id in each row to the five values in the IN list.
FROM TABLE
FROM TABLE shows that a PARTITION SCAN operator is reading a database
table. A second message gives the table name, and, if there is a correlation
name, it is printed on the next line. Under the FROM TABLE message in the
previous example output, sysobjects is the table name and s is the correlation
name. The previous example also shows additional messages under the FROM
TABLE message. These messages give more information about how the
PARTITION SCAN operator is directing the access layer of Adaptive Server to
get the rows from the table being scanned.
The messages below indicate whether the scan is a table scan or an index scan:
• Table Scan – the rows are fetched by reading the pages of the table.
• Using Clustered Index – a clustered index is used to fetch the rows
of the table.
• Index: indexname – an index is used to fetch the table rows. If this message
is not preceded by “using clustered index,” a nonclustered index is
used. indexname is the name of the index that will be used.
These messages indicate the direction of a table or index scan. The scan
direction depends on the ordering specified when the indexes were created and
the order specified for columns in the order by clause or other useful orderings
that can be exploited by operators further up in the query plan (for example, a
sorted ordering for a merge-join strategy).
Backward scans can be used when the order by clause contains the ascending
or descending qualifiers on index keys, exactly opposite of those in the create
index clause.
Forward scan
Backward scan
The scan-direction messages are followed by positioning messages, which
describe how access to a table or to the leaf level of an index takes place:
• Positioning at start of table – a table scan that starts at the first
row of the table and goes forward.
• Positioning at end of table – a table scan that starts at the last row
of the table and goes backward.
• Positioning by key – the index is used to position the scan at the first
qualifying row.
• Positioning at index start/positioning at index end – these
messages are similar to the corresponding messages for table scans, except
that an index is being scanned instead of a table.
If the scan can be limited due to the nature of the query, the following messages
describe how:
• Scanning only the last page of the table – appears when the
scan uses an index and is searching for the maximum value for scalar
aggregation. If the index is on the column whose maximum is sought, and
the index values are in ascending order, the maximum value will be on the
last page.
• Scanning only up to the first qualifying row – appears when
the scan uses an index and is searching for the minimum value for scalar
aggregation.
Note If the index key is sorted in descending order, the above messages for
minimum and maximum aggregates are reversed.
In some cases, the index being scanned contains all of the columns of the table
that are needed in the query. In such a case, this message is printed:
Index contains all needed columns. Base table will not
be read.
If an index contains all the columns needed by the query, the optimizer may
choose an Index Scan over a Table Scan even though there are no useful
keys on the index columns. The amount of I/O required to read the index can
be significantly less than that required to read the base table. Index scans that
do not require base table pages to be read are called covered index scans.
If an index scan is using keys to position the scan, this message prints:
Keys are:
Key <ASC/DESC>
This message shows the names of the columns used as keys (each key on its
own output line) and shows the index ordering on that key: ASC for ascending
and DESC for descending.
After the messages that describe the type of access being used by the scan
operator, messages about the I/O sizes and buffer cache strategy are printed.
set showplan on
go
select au_fname, au_lname, au_id from authors
where au_lname = "Williams"
go
RID scan
The Positioning by Row IDentifier (RID) scan is found only in query
plans that use the second or strategy that the optimizer can choose, the general
or strategy. The general or strategy may be used when multiple or clauses are
present on different columns. An example of a query for which the optimizer
can choose a general or strategy and its showplan output is:
use pubs2
go
set showplan on
go
select id from sysobjects where id = 4 or name = 'foo'
| | | sysobjects
| | | Using Dynamic Index.
| | | Forward Scan.
| | | Positioning by Row IDentifier (RID).
| | | Using I/O Size 2 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for data pages.
In this example, the where clause contains two disjunctions, each on a different
column (id and name). There are indexes on each of these columns (csysobjects
and ncsysobjects), so the optimizer chose a query plan that uses an index scan
to find all rows whose id column is 4 and another index scan to find all rows
whose name is “foo.”
Since it is possible that a single row has both an ID of 4 and a name of “foo,”
that row would appear twice in the result set. To eliminate these duplicate rows,
the index scans return only the row identifiers (RIDs) of the qualifying rows.
The two streams of RIDs are concatenated by the HASH UNION operator, which
also removes any duplicate RIDs.
]The stream of unique RIDs is passed to the RID JOIN operator. The rid join
operator creates a worktable and fills it with a single-column row with each
RID. The RID JOIN operator then passes its worktable of RIDs to the RID
SCAN operator. The RID SCAN operator passes the worktable to the access
layer, where it is treated as a keyless nonclustered index and the rows
corresponding to the RIDs are fetched and returned.
The last SCAN in the showplan output is the RID SCAN. As can be seen from
the example output, the RID SCAN output contains many of the messages
already discussed above, but it also contains two messages that are printed only
for the RID SCAN:
• Using Dynamic Index – indicates the SCAN is using the worktable with
RIDs that was built during execution by the RID JOIN operator as an
index to locate the matching rows.
• Positioning by Row Identifier (RID) – indicates the rows are
being located directly by the RID.
Log Scan
Log Scan appears only in triggers that access inserted or deleted tables. These
tables are dynamically built by scanning the transaction log when the trigger is
executed. Triggers are executed only after insert, delete, or update queries
modify a table with a trigger defined on it for the specific query type. The
following example is a delete query on the titles table, which has a delete trigger
called deltitle defined on it:
use pubs2
go
set showplan on
go
delete from titles where title_id = 'xxxx'
STEP 1
The type of query is DELETE.
STEP 1
STEP 1
The type of query is ROLLBACK TRANSACTION.
STEP 1
The type of query is PRINT.
The procedure that defines the deltitle trigger consists of four SQL statements.
Use sp_helptext deltitle to display the text of deltitle. The first statement in
deltitle has been compiled into a query plan, the other three statements are
compiled into legacy query plans and are executed by the procedural execution
engine, not the query execution engine.
The showplan output for the SCAN operator for the titles table indicates that it is
doing a scan of the log by printing Log Scan.
STEP 1
The type of query is DELETE.
STEP 1
The type of query is ALTER TABLE.
The INSERT operator calls on its left child operator, the SCAN of t1, to read the
rows of t1, and builds new rows with only the c1 and c3 columns inserted into
#syb_altab. When all the new rows have been inserted into #syb_altab, the
INSERT operator calls on its right child, the TEXT DELETE operator, to delete
the text page chains for the c2 columns that have been dropped from t1.
Postprocessing replaces the original pages of t1 with those of #syb_altab to
complete the alter table command.
The TEXT DELETE operator appears only in alter table commands that drop
some, but not all text columns of a table, and it always appears as the right child
of an INSERT operator.
The TEXT DELETE operator displays the update mode message, exactly like
the INSERT, UPDATE, and DELETE operators.
STEP 1
The type of query is INSERT.
| |
| | |SCAN Operator (VA = 0)
| | | FROM TABLE
| | | publishers
| | | Index : publishers_6240022232
| | | Forward Scan.
| | | Positioning by key.
| | | Index contains all needed columns. Base table will not be
read.
| | | Keys are:
| | | pub_id ASC
| | | Using I/O Size 2 Kbytes for index leaf pages.
| | | With LRU Buffer Replacement Strategy for index leaf pages.
|
| TO TABLE
| titles
| Using I/O Size 2 Kbytes for data pages.
In the query plan, the INSERT operator’s left child operator is a CACHE SCAN,
which returns the row of values to be inserted into titles. The INSERT
operator’s right child is a DIRECT RI FILTER operator.
The DIRECT RI FILTER operator executes a scan of the publishers table to
find a row with a value of pub_id that matches the value of pub_id to be inserted
into titles. If a matching row is found, the DIRECT RI FILTER operator allows
the insert to proceed, but if a matching value of pub_id is not found in
publishers, the DIRECT RI FILTER operator aborts the command.
In this example, the DIRECT RI FILTER can check and enforce the referential
integrity constraint on titles for each row that is inserted, as it is inserted.
The next example shows a DIRECT RI FILTER operating in a different mode,
together with a DEFERRED RI FILTER operator:
use pubs3
go
set showplan on
go
update publishers set pub_id = '0001'
STEP 1
The type of query is UPDATE.
| | |
| | | TO TABLE
| | | Worktable1.
|
| |DEFERRED RI FILTER Operator has (VA = 12) 1 children.
| |
| | |SQFILTER Operator (VA = 11) has 2 children.
| | |
| | | |SCAN Operator (VA = 8)
| | | | FROM TABLE
| | | | Worktable1.
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Using I/O Size 2 Kbytes for data pages.
| | | | With LRU Buffer Replacement Strategy for data pages.
| | |
| | | Run subquery 1 (at nesting level 0).
| | |
| | | QUERY PLAN FOR SUBQUERY 1 (at nesting level 0 and at line 0).
| | |
| | | Non-correlated Subquery.
| | | Subquery under an EXISTS predicate.
| | |
| | | |SCALAR AGGREGATE Operator (VA = 10)
| | | | Evaluate Ungrouped ANY AGGREGATE.
| | | | Scanning only up to the first qualifying row.
| | | |
| | | | |SCAN Operator (VA = 9)
| | | | | FROM TABLE
| | | | | publishers
| | | | | Index : publishers_6240022232
| | | | | Forward Scan.
| | | | | Positioning by key.
| | | | | Index contains all needed columns. Base table will
not be read.
| | | | | Keys are:
| | | | | pub_id ASC
| | | | | Using I/O Size 2 Kbytes for index leaf pages.
| | | | | With LRU Buffer Replacement Strategy for index leaf
pages.
| | |
| | | END OF QUERY PLAN FOR SUBQUERY 1.|
| TO TABLE
| publishers
| Using I/O Size 2 Kbytes for data pages.
The referential integrity constraint on titles requires that for every value of
titles.pub_id there must exist a value of publishers.pub_id. However, this
example query is changing the values of publisher.pub_id, so a check must be
made to maintain the referential integrity constraint.
The example query can change the value of publishers.pub_id for several rows
in publishers, so a check to make sure that all of the values of titles.pub_id still
exist in publisher.pub_id cannot be done until all rows of publishers have been
processed.
This example calls for deferred referential integrity checking: as each row of
publishers is read, the UPDATE operator calls upon the DIRECT RI FILTER
operator to search titles for a row with the same value of pub_id as the value that
is about to be changed. If a row is found, it indicates that this value of pub_id
must still exist in publishers to maintain the referential integrity constraint on
titles, so the value of pub_id is inserted into WorkTable1.
After all of the rows of publishers have been updated, the UPDATE operator calls
upon the DEFERRED RI FILTER operator to execute its subquery to verify that
all of the values in Worktable1 still exist in publishers. The left child operator of
the DEFERRED RI FILTER is a SCAN which reads the rows from Worktable1.
The right child is a SQFILTER operator that executes an existence subquery to
check for a matching value in publishers. If a matching value is not found, the
command is aborted.
The examples in this section used simple referential integrity constraints,
between only two tables. Adaptive Server allows up to 192 constraints per
table, so it can generate much more complex query plans. When multiple
constraints must be enforced, there is still only a single DIRECT RI FILTER
or DEFERRED RI FILTER operator in the query plan, but these operators can
have multiple subplans, one for each constraint that must be enforced.
JOIN operators
Adaptive Server provides four primary JOIN operator strategies: NESTED
LOOP JOIN, MERGE JOIN, HASH JOIN, and NARY NESTED LOOP JOIN,
which is a variant of NESTED LOOP JOIN. In versions earlier than 15.0,
NESTED LOOP JOIN was the primary JOIN strategy. MERGE JOIN was also
available, but was, by default, not enabled.
Each JOIN operator is described in further detail below, including a general
description of the each algorithm. These descriptions give a high-level
overview of the processing required for each JOIN strategy.
STEP 1
The type of query is SELECT.
| | au_lname ASC
| | Using I/O Size 2 Kbytes for index leaf pages.
| | With LRU Buffer Replacement Strategy for index leaf pages.
| | Using I/O Size 2 Kbytes for data pages.
| | With LRU Buffer Replacement Strategy for data pages.
|
| |SCAN Operator (VA = 1)
| | FROM TABLE
| | titleauthor
| | ta
| | Using Clustered Index.
| | Index : taind
| | Forward Scan.
| | Positioning by key.
| | Keys are:
| | au_id ASC
| | Using I/O Size 2 Kbytes for data pages.
| | With LRU Buffer Replacement Strategy for data pages.
The authors table is joined with the titleauthor table. A NESTED LOOP JOIN
strategy has been chosen. The NESTED LOOP JOIN operator’s type is “Inner
Join.” First, the authors table is opened and positioned on the first row (using
the aunmind index) containing an l_name value of “Bloom.” Then, the
titleauthor table is opened and positioned on the first row with an au_id equal to
the au_id value of the current authors’ row using the clustered index “taind.” If
there is no useful index for lookups on the inner stream, the optimizer may
generate a reformatting strategy.
Generally, a NESTED LOOP JOIN strategy is effective when there is a useful
index available for qualifying the join predicates on the inner stream.
MERGE JOIN
The MERGE JOIN operator is a binary operator. The left and right children are
the outer and inner data streams, respectively. Both data streams must be sorted
on the MERGE JOIN’s key values.
First, a row from the outer stream is fetched. This initializes the MERGE JOIN’s
join key values. Then, rows from the inner stream are fetched until a row with
key values that match or are greater than (less than if key column is
descending) is encountered. If the join key matches, the qualifying row is
passed on for additional processing, and a subsequent next call to the MERGE
JOIN operator continues fetching from the currently active stream.
If the new values are greater than the current comparison key, these values are
used as the new comparison join key while fetching rows from the other
stream. This process continues until one of the data streams is exhausted.
Generally, the MERGE JOIN strategy is effective when a scan of the data
streams requires that most of the rows must be processed, and that, if any of the
input streams are large, they are already sorted on the join keys.
select ta.title_id
from titleauthor ta, authors a
where a.au_id = ta.au_id
and au_lname = "Bloom"
go
STEP 1
The type of query is EXECUTE.
Executing a newly cached statement.
STEP 1
The type of query is SELECT.
HASH JOIN
The HASH JOIN operator is a binary operator. The left child generates the build
input stream. The right child generates the probe input stream. The build set is
generated by completely draining the build input stream when the first row is
requested from the HASH JOIN operator. Every row is read from the input
stream and hashed into an appropriate bucket using the hash key.
If there is not enough memory to hold the entire build set, then a portion of it
spills to disk. This portion is referred to as a hash partition and should not be
confused with table partitions. A hash partition consists of a collection of hash
buckets. After the entire left child’s stream has been drained, the probe input is
read.
Each row from the probe set is hashed. A lookup is done in the corresponding
build bucket to check for rows with matching hash keys. This occurs if the
build set’s bucket is memory resident. If it has been spilled, the probe row is
written to the corresponding spilled probe partition. When a probe row’s key
matches a build row’s key, then the necessary projection of the two row’s
columns is passed up for additional processing.
Spilled partitions are processed in subsequent recursive passes of the HASH
JOIN algorithm. New hash seeds are used in each pass so that the data is
redistributed across different hash buckets. This recursive processing continues
until the last spilled partition is completely memory resident. When a hash
partition from the build set contains many duplicates, the HASH JOIN operator
reverts back to NESTED LOOP JOIN processing.
Generally, the HASH JOIN strategy is good in cases where most of the rows
from the source sets must be processed and there are no inherent useful
orderings on the join keys or there are no interesting orderings that can be
promoted to calling operators (for example, an order by clause on the join key).
HASH JOINs perform particularly well if one of the data sets is small enough
to be memory resident. In this case, no spilling occurs and no I/O is needed to
perform that HASH JOIN algorithm.
select ta.title_id
from titleauthor ta, authors a
where a.au_id = ta.au_id
and au_lname = "Bloom"
ROOT:EMIT Operator
Only rows with an au_lname value of “Bloom” are returned from this scan.
These rows are then hashed on their au_id value and placed into their
corresponding hash bucket. After the initial build phase is completed, the probe
stream is opened and scanned. Each row from the source index,
titleauthor.auidind, is hashed on the au_id column. The resulting hash value is
used to determine which bucket in the build set should be searched for
matching hash keys. Each row from the build set’s hash bucket is compared to
the probe row’s hash key for equality. If the row matches, the titleauthor.au_id
column is returned to the EMIT operator.
The HASH JOIN operator’s showplan output contains a message indicating the
worktable to be used for the spilled partition’s backing store. The input row
width is limited to 64 kilobytes.
NARY NESTED LOOP JOIN execution has a performance benefit over the
execution of a series of NESTED LOOP JOIN operators. The example below
demonstrates a fundamental difference between the two methods of execution.
With a series of NESTED LOOP JOIN, a scan may eliminate rows based on
searchable argument values initialized by an earlier scan. That scan may not be
the one that immediately preceded the failing scan. With a series of NESTED
LOOP JOINs, the previous scan would be completely drained although it has
no effect on the failing scan. This could result in a significant amount of
needless I/O. With NARY NESTED LOOP JOINs, the next row fetched comes
from the scan that produced the failing searchable argument value, which is far
more efficient.
select a.au_id, au_fname, au_lname
from titles t, titleauthor ta, authors a
where a.au_id = ta.au_id
and ta.title_id = t.title_id
and a.au_id = t.title_id
and au_lname = "Bloom"
QUERY PLAN FOR STATEMENT 1 (at line 1).
STEP 1
The type of query is SELECT.
NestLoopJoin
InnerJoin
(VA = 5)
NestLoopJoin IndexScan
InnerJoin titleidind (t2)
(VA = 3) (VA = 4)
IndexScan Restrict
aunmid (a) (0) (0) (4) (0)
(VA = 0) (VA = 2)
IndexScan
auidind (ta)
(VA = 1)
All query processor operators are assigned a virtual address. The lines in
Figure 2-3 with VA = report the virtual address for a given operator.
The effective join order is authors, titleauthor, titles. A RESTRICT operator is the
parent operator of the scan on titleauthors. This plan is transformed into the
NARY NESTED LOOP JOIN plan below:
EMIT
(VA=6)
NaryNLJoin
(VA = 4)
NaryNLJoin
(VA = 4)
IndexScan
(VA = 0)
aunmid (a) RESTRICT
(VA = 2) NaryNLJoin
(0) (0) (4) (0) (VA = 4)
IndexScan IndexScan
(VA = 1) (VA = 3)
auidind(ta) titleidind(t)
The transformation retains the original join order of authors, titleauthor, and
titles. In this example, the scan of titles has two searchable arguments on it—
ta.title_id = t.title_id and a.au_id = t.title_id. So, the scan of titles fails because of
the searchable argument value established by the scan of titleauthor, or it fails
because of the searchable argument value established by the scan of authors. If
no rows are returned from a scan of titles because of the searchable argument
value set by the scan of authors, there is no point in continuing the scan of
titleauthor. For every row fetched from titleauthor, the scan of titles fails. It is
only when a new row is fetched from authors that the scan of titles might
succeed. This is why NARY NESTED LOOP JOINs have been implemented;
they eliminate the useless draining of tables that have no impact on the rows
returned by successive scans.
In the example, the NARY NESTED LOOP JOIN operator closes the scan of
titleauthor, fetches a new row from authors, and repositions the scan of
titleauthor based on the au_id fetched from authors. Again, this can be a
significant performance improvement as it eliminates the needless draining of
the titleauthor table and the associated I/O that could occur.
semijoin
The semijoin is a variant of NESTED LOOP JOIN operator, and includes the
NESTED LOOP JOIN operator in its result set. When you make a semi-join
between two tables, Adaptive Server returns the rows from the first table that
contain one or more matches in the second table (a regular join returns the
matching rows from the first table only once). That is, instead of scanning a
table to return all matching values, an semijoin returns rows when it finds the
first matching value and then stops processing. Semijoins are also known as
“existence joins.”
For example, if you perform a semijoin on the titles and titleauthor tables:
select title
from titles
where title_id in (select title_id from titleauthor)
and title like "A Tutorial%"
QUERY PLAN FOR STATEMENT 1 (at line 1).
STEP 1
The type of query is SELECT.
| | | Forward Scan.
| | | Positioning by key.
| | | Index contains all needed columns. Base table will not be read.
| | | Keys are:
| | | title_id ASC
| | | Using I/O Size 2 Kbytes for index leaf pages.
| | | With LRU Buffer Replacement Strategy for index leaf pages.
Distinct operators
There are three unary operators you can use to enforce distinctness: GROUP
SORTED Distinct, SORT Distinct, and HASH Distinct. Each has
advantages and disadvantages. The optimizer chooses an efficient distinct
operator with respect to its use within the entire query plan’s context.
See Table 1-3 on page 23 for a list and description of all query processor
operators.
STEP 1
The type of query is SELECT.
STEP 1
The type of query is SELECT.
|SORT Operator
| Using Worktable1 for internal storage.
|
| |SCAN Operator
| | FROM TABLE
| | authors
| | Table Scan.
| | Forward Scan.
| | Positioning at start of table.
| | Using I/O Size 2 Kbytes for data pages.
| | With LRU Buffer Replacement Strategy for data pages.
The scan of the authors table does not return rows sorted on the distinct key
columns. This requires that a SORT Distinct operator be used rather than a
GROUP SORTED Distinct operator. The SORT operator’s distinct key
columns are au_lname and au_fname. The showplan output indicates that
Worktable1 is used for disk storage in case the input set does not fit entirely in
memory.
STEP 1
The type of query is SELECT.
STEP 1
The type of query is SELECT.
| | | authors
| | | Table Scan.
| | | Forward Scan.
| | | Positioning at start of table.
| | | Using I/O Size 2 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for data pages.
In this query plan, the scan of authors does not return rows in grouping order.
A SORT operator is applied to order the stream based on the grouping column
city. At this point, a GROUP SORTED COUNT AGGREGATE operator can be
applied to evaluate the count aggregate.
The GROUP SORTED COUNT AGGREGATE operator showplan output reports the
aggregate functions being applied as:
| Evaluate Grouped COUNT AGGREGATE.
STEP 1
The type of query is SELECT.
GROUP INSERTING
GROUP INSERTING is a blocking operator. All rows from the child operator
must be processed before the first row can be returned from the GROUP
INSERTING.
plan
'(group_inserting (i_scan auidind authors ))'
QUERY PLAN FOR STATEMENT 1 (at line 1).
Optimized using the Abstract Plan in the PLAN clause.
STEP 1
The type of query is SELECT.
compute by message
Processing is done in the EMIT operator, and requires that the EMIT operator’s
input stream be sorted according to any order by requirements in the query. The
processing is similar to what is done in the GROUP SORTED AGGREGATE
operator.
Each row read from the child is checked to see if it starts a new group. If it does
not, aggregate functions are applied as appropriate to the query’s requested
groups. If a new group is started, the current group and its aggregated values
are returned to the user. A new group is then started and its aggregate values
are initialized from the new row’s values. This example collects an ordered list
of all cities and reports a count of the number of entries for each city after the
city list.
select city
from authors
order by city
compute count(city) by city
STEP 1
The type of query is SELECT.
Union operators
The UNION ALL operator is a nary operator that displays this message:
UNION ALL OPERATOR has N children.
N is the number of input streams into the operator.
This example demonstrates the use of UNION ALL:
select * from sysindexes where id < 100
union all
select * from sysindexes where id > 200
| |
| | |SCAN Operator (VA = 1)
| | | FROM TABLE
| | | sysindexes
| | | Using Clustered Index
| | | Index : csysindexes
| | | Forward scan.
| | | Positioning by key.
| | | Keys are:
| | | id ASC
| | | Using I/O Size 2 Kbytes for index leaf pages.
| | | With LRU Buffer Replacement Strategy for index leaf pages.
| | | Using I/O Size 2 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for data pages.
The UNION ALL operator starts by fetching all rows from its leftmost child. In
this example, it returns all of the sysindexes rows with an ID less than 100. As
each child operator’s datastream is emptied, the UNION ALL operator moves
on to the child operator immediately to its right. This stream is opened and
emptied. This continues until the last (the Nth) child operator is emptied.
HASH UNION
The HASH UNION operator uses Adaptive Server hashing algorithms to
simultaneously perform a UNION ALL operation on several data streams and
hash-based duplicate elimination.
The HASH UNION operator is a nary operator that displays this message:
HASH UNION OPERATOR has <N> children.
<N> is the number of input streams into the operator.
HASH UNION also displays the name of the worktable it uses, in this format:
HASH UNION OPERATOR Using Worktable <X> for internal
storage.
This worktable is used by the HASH UNION operator to temporarily store data
for the current iteration that cannot be processed in the memory currently
available.
This example demonstrates the use of HASH UNION:
select * from sysindexes
union
select * from sysindexes
STEP 1
The type of query is SELECT.
STEP 1
The type of query is SELECT.
RESTRICT operator
The RESTRICT operator is a unary operator that evaluates expressions based
on column values. The RESTRICT operator is associated with multiple column
evaluations lists that can be processed before fetching a row from the child
operator, after fetching a row from the child operator, or to compute the value
of virtual columns after fetching a row from the child operator.
SORT operator
The SORT operator has only one child operator within the query plan. Its role
is to generate an output data stream from the input stream, using a specified
sorting key.
The SORT operator may execute a streaming sort when possible, but may also
have to store results temporarily into a worktable. The SORT operator displays
the worktable’s name in this format:
Using Worktable<N> for internal storage.
where <N> is a numeric identifier for the worktable within the showplan output.
Here is an example of a simple query plan using a SORT operator and a
worktable:
select au_id from authors order by postalcode
STEP 1
The type of query is SELECT.
STORE operator
The STORE operator is used to create a worktable, fill it, and possibly create an
index on it. As part of the execution of a query plan, the worktable is used by
other operators in the plan. A SEQUENCER operator guarantees that the plan
fragment corresponding to the worktable and potential index creation is
executed before other plan fragments that use the worktable. This is important
when a plan is executed in parallel, because execution processes operate
asynchronously.
Reformatting strategies use the STORE operator to create a worktable with a
clustered index on it.
If the STORE operator is used for a reformatting operation, it prints this
message:
Worktable <X> created, in <L> locking mode for
reformatting.
The locking mode <L> has to be one of “allpages,” “datapages,” or “datarows.”
The STORE operator also prints this message:
Creating clustered index.
If the STORE operator is not used for a reformatting operation, it prints this
message:
Worktable <X> created, in <L> locking mode.
The following example applies to the STORE operator, as well as to the
SEQUENCER operator.
select * from bigun a, bigun b where a.c4 = b.c4 and a.c2 < 10
STEP 1
SEQUENCER operator
The SEQUENCER operator is a nary operator used to sequentially execute each
the child plans below it. The SEQUENCER operator is used in reformatting plans,
and certain aggregate processing plans.
The SEQUENCER operator executes each of its child subplans, except for the
rightmost one. Once all the left child subplans are executed, the rightmost
subplan is executed.
The SEQUENCER operator displays this message:
SEQUENCER operator has N children.
select * from tab1 a, tab2 b where a.c4 = b.c4 and a.c2 < 10
QUERY PLAN FOR STATEMENT 1 (at line 1).
Optimized using the Abstract Plan in the PLAN clause.
STEP 1
The type of query is SELECT.
| | | | | FROM TABLE
| | | | | tab2
| | | | | b
| | | | | Table Scan.
| | | | | Forward Scan.
| | | | | Positioning at start of table.
| | | | | Using I/O Size 2 Kbytes for data pages.
| | | | | With LRU Buffer Replacement Strategy for data pages.
| | | |
| | | | TO TABLE
| | | | Worktable1.
| |
| | |NESTED LOOP JOIN Operator (Join Type: Inner Join) (VA = 3)
| | |
| | | |SCAN Operator (VA = 2)
| | | | FROM TABLE
| | | | tab1
| | | | a
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Using I/O Size 2 Kbytes for data pages.
| | | | With LRU Buffer Replacement Strategy for data pages.
| | |
| | | |SCAN Operator (VA = 1)
| | | | FROM TABLE
| | | | Worktable1.
| | | | Using Clustered Index.
| | | | Forward Scan.
| | | | Positioning by key.
| | | | Using I/O Size 2 Kbytes for data pages.
| | | | With LRU Buffer Replacement Strategy for data pages.
In this example, the SEQUENCER operator implements a reformatting strategy.
The leftmost branch of the SEQUENCER operator creates a clustered index on
Worktable1. This branch is executed and closed before the SEQUENCER
operator proceeds to the next child operator. The SEQUENCER operator arrives
at the rightmost child, opens, and begins to drain it, returning rows back to its
parent operator. The design intent of the SEQUENCER operator is for operators
in the rightmost branch to use the worktables created in the preceding outer
branches of the SEQUENCER operator. In this example, Worktable1 is used in a
nested-loop join strategy. The scan of Worktable1 is positioned by a key on its
clustered index for each row that comes from the outer scan of tab1.
SCROLL operator
The SCROLL operator encapsulates the functionality of scrollable cursors in
Adaptive Server. Scrollable cursors may be insensitive, meaning that they
display a snapshot of their associated data, taken when the cursor is opened, or
semi-sensitive, meaning that the next rows to be fetched are retrieved from the
live data.
The SCROLL operator is a unary operator that displays this message:
SCROLL OPERATOR ( Sensitive Type: <T>)
The type may be insensitive or semi-sensitive.
This is an example of a plan featuring an insensitive scrollable cursor:
declare CI insensitive scroll cursor for
select au_lname, au_id from authors
go
set showplan on
go
open CI
STEP 1
The type of query is OPEN CURSOR CI.
STEP 1
The type of query is DECLARE CURSOR.
STEP 1
The type of query is SELECT.
Performance and Tuning Series: Query Processing and Abstract Plans 101
Union operators
SQLFILTER operator
The SQLFILTER operator is a nary operator that executes subqueries. Its
leftmost child represents the outer query, and the other children represent query
plan fragments associated with one or more subqueries.
The leftmost child generates correlation values that are substituted into the
other child plans.
The SQLFILTER operator displays this message:
SQFILTER Operator has <N> children.
This example illustrates the use of SQLFILTER:
select pub_name from publishers
where pub_id =
(select distinct titles.pub_id from titles
STEP 1
The type of query is SELECT.
Performance and Tuning Series: Query Processing and Abstract Plans 103
Union operators
The SQLFILTER operator has two children in this example. The leftmost child
is the query’s outer block. It is a simple scan of the publishers table. The right
child is used to evaluate the query’s subquery. SQLFILTER fetch rows from the
outer block. For every row from the outer block, SQLFILTER invokes the right
child to evaluate the subquery. If the subquery evaluates to TRUE, a row is
returned to the SQLFILTER’s parent operator.
EXCHANGE operator
The EXCHANGE operator is a unary operator that encapsulates parallel
processing of Adaptive Server SQL queries. EXCHANGE can be located almost
anywhere in a query plan and divides the query plan into plan fragments. A
plan fragment is a query plan tree that is rooted at an EMIT or EXCHANGE:EMIT
operator and has leaves that are SCAN or EXCHANGE operators. A serial plan is
a plan fragment that is executed by a single process.
An EXCHANGE operator’s child operator is always an EXCHANGE:EMIT
operator. EXCHANGE:EMIT is the root of a new plan fragment. An EXCHANGE
operator has an associated server process called the Beta process that acts as a
local execution coordinator for the EXCHANGE operator’s worker processes.
Worker processes execute the plan fragment as directed by the parent
EXCHANGE operator and its Beta process. The plan fragment is often executed
in a parallel fashion, using two or more processes. The EXCHANGE operator and
Beta process coordinate activities, including the exchange of data between the
fragment boundaries.
The topmost plan fragment, rooted at an EMIT operator rather than an
EXCHANGE:EMIT operator, is executed by the Alpha process. The Alpha
process is a consumer process associated with the user connection. The Alpha
process is the global coordinator of all of the query plan’s worker processes. It
is responsible for initially setting up all of the plan fragment’s worker processes
and eventually freeing them. It manages and coordinates all of the fragment’s
worker processes in the case of an exception.
The EXCHANGE operator displays this message:
Executed in parallel by N producer and P consumer processes.
The number of producers refers to the number of worker processes that execute
the plan fragment located beneath the EXCHANGE operator. The number of
consumers refers to the number of worker processes that execute the plan
fragment that contains the EXCHANGE operator. The consumers process the data
passed to them by the producers. Data is exchanged between the producer and
consumer processes through a pipe set up in the EXCHANGE operator. The
producer’s EXCHANGE:EMIT operator writes rows into the pipe while
consumers read rows from this pipe. The pipe mechanism synchronizes
producer writes and consumer reads such that no data is lost.
This example illustrates a parallel query in the master database against the
system table sysmessages:
use master
go
set showplan on
go
select count(*) from sysmessages t1 plan '(t_scan t1) (prop t1 (parallel 4))
ROOT:EMIT Operator
|SCALAR AGGREGATE Operator
| Evaluate Ungrouped COUNT AGGREGATE.
|
| |EXCHANGE Operator
| |Executed in parallel by 4 Producer and 1 Consumer processes.
| |
| | |EXCHANGE:EMIT Operator
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | sysmessages
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Executed in parallel with a 4-way hash scan.
| | | | Using I/O Size 4 Kbytes for data pages.
| | | | With LRU Buffer Replacement Strategy for data pages.
Performance and Tuning Series: Query Processing and Abstract Plans 105
INSTEAD-OF TRIGGER operators
There are two plan fragments in this example. The first fragment in any plan,
parallel or not, is always rooted by an EMIT operator. The first fragment in this
example consists of the EMIT, SCALAR AGGREGATE, and EXCHANGE operators.
This first fragment is always executed by the single Alpha process. In this
example, it also acts as the Beta process responsible for managing the
EXCHANGE operator’s worker processes.
The second plan fragment is rooted at the EXCHANGE:EMIT operator. Its only
child operator is the SCAN operator. The SCAN operator is responsible for
scanning the sysmessages table. The scan is executed in parallel:
Executed in parallel with a 4-way hash scan
This indicates that each worker process is responsible for approximately a
quarter of the table. Pages are assigned to the worker processes based on
having the data page ID.
The EXCHANGE:EMIT operator writes data rows to the consumers by writing to
a pipe created by its parent EXCHANGE operator. In this example, the pipe is a
four-to-one demultiplexer, and include several pipe types that perform quite
different behaviors.
STEP 1
The type of query is SELECT.
Performance and Tuning Series: Query Processing and Abstract Plans 107
INSTEAD-OF TRIGGER operators
STEP 1
The type of query is SELECT.
open curs1
go
fetch curs1
c1 c2
_________ ________
1 2
(1 row affected)
set showplan on
go
update t12view set c1 = 3
where current of curs1
STEP 1
The type of query is SELECT.
STEP 1
The type of query is SELECT.
Performance and Tuning Series: Query Processing and Abstract Plans 109
INSTEAD-OF TRIGGER operators
|
| |SCAN Operator (VA = 0)
| | FROM CACHE
The showplan statement above is for the trigger’s statement. It is identical to
the output in the INSTEAD-OF TRIGGER example.
Note Each option specified must be followed by one of normal, brief, long,
on, or off. on and normal are equivalent. Each show option must include
one of these choices (normal, brief, and so on); specify more than one
option in a single set option command by separating each option or choice
pair with commas.
See “Diagnostic usage scenarios” on page 119 for examples of using the
set options.
Performance and Tuning Series: Query Processing and Abstract Plans 111
set commands for XML format messages
Option Definition
show_exec_xml Gets the compiled plan output in XML, showing each of the query plan operators.
show_opt_xml Gets optimizer diagnostic output, which shows the different components such as
logical operators, output from the managers, some of the search engine diagnostics,
and the best query plan.
show_execio_xml Gets the plan output along with estimated and actual I/Os. show_execio_xml also
includes the query text.
show_lop_xml Gets the output logical operator tree in XML.
show_managers_xml Shows the output of the different component managers during the preparation phase
of the query optimizer.
show_log_props_xml Shows the logical properties for a given equivalence class (one or more groups of
relations in the query).
show_parallel_xml Shows the diagnostics related to the optimizer while generating parallel query plans.
show_histograms_xml Shows diagnostics related to histograms and the merging of histograms.
show_final_plan_xml Gets the plan output. Does not include the estimated and actual I/Os.
show_final_plan_xml includes the query text.
show_abstract_plan_xml Shows the generated abstract plan.
show_search_engine_xml Shows diagnostics related to the search engine.
show_counters_xml Shows plan object construction/destruction counters.
show_best_plan_xml Shows the best plan in XML.
show_pio_costing_xml Shows actual physical input/output costing in XML.
show_lio_costing_xml Shows actual logical input/output costing in XML.
show_elimination_xml Shows partition elimination in XML.
client When specified, output is sent to the client. By default, this is the error log. When trace
flag 3604 is active, however, output is sent to the client connection.
message When specified, output is sent to an internal message buffer.
Performance and Tuning Series: Query Processing and Abstract Plans 113
set commands for XML format messages
query_num refers to the number of queries that are cached in the buffer.
Currently, a maximum of 20 queries are cached in the buffer. The cache stops
collecting query plans when it reaches 20 queries; it ignores the rest of the
query plans. However, the message buffer continues to collect query plans.
After 20 queries, you can display the message buffer only in its entirety by
using a value of 0.
Valid values for query_num are 1 – 20, -1, and 0 (zero). A value of -1 refers to
the last XML doc in the cache; a value of 0 refers to the entire message buffer.
The message buffer may overflow. If this occurs, there is no way to log all of
the XML document, which may result in a partial and invalid XML document.
When the message buffer is accessed using showplan_in_xml, the buffer is
emptied after execution.
You may want to use set textsize to set the maximum text size, as the XML
document is printed as a text column and the document is truncated if the
column is not large enough. For example, set the text size to 100000 bytes
using:
set textsize 100000
When set plan is issued with off, all XML tracing is turned off if all of the trace
options have been turned off. Otherwise, only specified options are turned off.
Other options previously turned on are still valid and tracing continues on the
specified destination stream. When you issue another set plan option, the
previous options are joined with the current options, but the destination stream
is switched unconditionally to a new one.
• The version level of the query plan. Each version of the plan is uniquely
identified. This is the first version of the plan:
<planVersion>1.0</planVersion>
• The statement number in a batch or stored procedure, along with the line
number of the statement in the original text. This is statement number 2,
but line number 6, in the query:
<statementNum>2</statementNum>
<lineNum>6</lineNum>
• The abstract plan for the query. For example, this is the abstract plan for
the query select * from titles:
<abstractPlan>
<![CDATA[>
( i_scan titleidind titles ) ( prop titles ( parallel 1
) ( prefetch 8 ) ( lru ) )
]]>
</abstractPlan>
• The logical I/O, physical I/O, and CPU costs:
<costs>
<lio> 2 </lio>
<pio> 2 </pio>
<cpu> 18 </cpu>
</costs>
You can estimate the total costs with this formula (the 25, 2, and 0.1 are
constants):
25 X pio + 2 X lio + 0.1 X cpu
• The estimated execution resource usage, including the number of threads
and auxiliary scan descriptors used by the query plan.
• The number of plans the query engine viewed and the plans it determined
were valid, the total time the query spent in the query engine (in
milliseconds), the time the query engine took to determine the first legal
plan, and the amount of procedure cache used during the optimization
process.
<optimizerMetrics>
<optTimeMs>6</optTimeMs>
<optTimeToFirstPlanMs>3</optTimeToFirstPlanMs>
<plansEvaluated>1</plansEvaluated>
<plansValid>1</plansValid>
<procCacheBytes>140231</procCacheBytes>
</optimizerMetrics>
• The last time update statistics was run on the current table and whether the
query engine used an estimation constant for a given column that it could
have estimated better if statistics were available. This section includes
information about columns with missing statistics:
<optimizerStatistics>
<statInfo>
<objName>titles</objName>
Performance and Tuning Series: Query Processing and Abstract Plans 115
set commands for XML format messages
<columnStats>
<column>title_id</column>
<updateTime>Oct 5 2006 4:40:14:730PM</updateTime>
</columnStats>
<columnStats>
<column>title</column>
<updateTime>Oct 5 2006 4:40:14:730PM</updateTime>
</columnStats>
</statInfo>
</optimizerStatistics>
• An operator tree that includes table and index scans with information
about cache strategies and I/O sizes (inserts, updates, and deletes have the
same information for the target table). The operator tree also shows
whether updates are performed in “direct” or “deferred” mode. The
exchange operator includes information about the number of producer and
consumer processes the query used.
<TableScan>
<VA>0</VA>
<est>
<rowCnt>18</rowCnt>
<lio>2</lio>
<pio>2</pio>
<rowSz>218.5555</rowSz>
</est>
<varNo>0</varNo>
<objName>titles</objName>
<scanType>TableScan</scanType>
<partitionInfo>
<partitionCount>1</partitionCount>
</partitionInfo>
<scanOrder> ForwardScan </scanOrder>
<positioning> StartOfTable </positioning>
<dataIOSizeInKB>8</dataIOSizeInKB>
<dataBufReplStrategy> LRU </dataBufReplStrategy>
</TableScan>
• The header section, which contains information about the cache statement,
such as the statement ID, object ID and the text:
<?xml version="1.0" encoding="UTF-8"?>
<query>
<statementId>1328134997</statementId>
<text>
<![CDATA[SQL Text: select name from sysobjects where id = 10]]>
</text>
If PlanID is set to 0, show_cached_plan_in_xml displays output for all
available plans associated with the cached statement.
• The plan section which contains the plan ID and these subsections:
• Parameter – returns the plan status, the parameters used to compile the
query, and the parameter values that caused the slowest performance.
<planId>11</planId>
<planStatus> available </planStatus>
<execCount>1371</execCount>
<maxTime>3</maxTime>
<avgTime>0</avgTime>
<compileParameters/>
<execParameters/>
• opTree – returns the operators tree, row count, and logical I/O (lio)
and physical I/O (pio) estimates for every operator. the opTree sub-
section returns query plan and optimizer estimates such as lio, pio and
row count
This is an example of an output for the Emit operator.
<opTree>
<Emit>
<VA>1</VA>
<est>
<rowCnt>10</rowCnt>
<lio>0</lio>
<pio>0</pio>
<rowSz>22.54878</rowSz>
</est>
<act>
<rowCnt>1</rowCnt>
</act>
<arity>1</arity>
<IndexScan>
<VA>0</VA>
<est>
Performance and Tuning Series: Query Processing and Abstract Plans 117
set commands for XML format messages
<rowCnt>10</rowCnt>
<lio>0</lio>
<pio>0</pio>
<rowSz>22.54878</rowSz>
</est>
<act>
<rowCnt>1</rowCnt>
<lio>3</lio>
<pio>0</pio>
</act>
<varNo>0</varNo>
<objName>sysobjects</objName>
<scanType>IndexScan</scanType>
<indName>csysobjects</indName>
<indId>3</indId>
<scanOrder> ForwardScan </scanOrder>
<positioning> ByKey </positioning>
<perKey>
<keyCol>id</keyCol>
<keyOrder> Ascending </keyOrder>
</perKey>
<indexIOSizeInKB>2</indexIOSizeInKB>
<indexBufReplStrategy> LRU </indexBufReplStrategy>
<dataIOSizeInKB>2</dataIOSizeInKB>
<dataBufReplStrategy> LRU </dataBufReplStrategy>
</IndexScan>
</Emit>
<opTree>
• execTree – returns the query plan with the operator internal details.
Details vary, depending on the operator. This is an example of an
output for the Emit operator.
<Emit>
<Details>
<VA>5</VA>
<Vtuple Label="Output Vtuple">
<collection Label="Columns (#2)">
<Column>
<0x0x1462d2838) type:GENERIC_TOKEN len:0 offset:0
valuebuf:0x(nil) status:(0x00000008 (STATNULL))
(constant:0x0x1462d24c0 type:INT4 len:4 maxlen:4 constat: (0x0004
(VARIABLE), 0x0002 (PARAM)))
</Column>
<Column>
(0x0x1462d2878) type:GENERIC_TOKEN len:0 offset:0
valuebuf:0x(nil) status:(0x00000008 (STATNULL))
Performance and Tuning Series: Query Processing and Abstract Plans 119
Diagnostic usage scenarios
Scenario B To get the execution plan, use the showplan_in_xml function. You can get the
output from the last query, or from any of the first 20 queries in a batch or
stored procedure.
set plan for show_opt_xml to message on
Run the query as:
select id from sysindexes where id < 0
select name from sysobjects where id > 0
go
select showplan_in_xml(0)
go
The example generates two XML documents as text streams. You can run an
XPath query over this built-in as long as the XML option is enabled in
Adaptive Server.
select xmlextract("/", showplan_in_xml(-1))
go
This allows the XPath query “/” to be run over the XML doc produced by the
last query.
Scenario C To set multiple options:
set plan for show_exec_xml, show_opt_xml to client on
go
go
select showplan_in_xml(-1)
go
showplan_in_xml() can also be part of the same batch as it works the same way.
Any message for the showplan_in_xml() function is ignored for logging.
To create a stored procedure:
create proc PP as
declare @v int
select @v = 1
select name from sysobjects where id = @v
go
exec PP
go
select showplan_in_xml(-1)
go
If the stored procedure calls another stored procedure, and the called stored
procedure compiles, and optimizer diagnostics are turned on, you get the
optimizer diagnostics for the new set of statements as well. The same is true if
show_execio_xml is turned on and only the called stored procedure is executed.
Scenario E To query the output of the showplan_in_xml() function for the query execution
plan, which is an XML doc:
set plan for show_exec_xml to message on
go
Performance and Tuning Series: Query Processing and Abstract Plans 121
Permissions for set commands
• @@plwpid global variable – returns the object ID of the next most recently
prepared lightweight procedure that corresponds to a dynamic SQL
prepare statement.
Performance and Tuning Series: Query Processing and Abstract Plans 123
Analyzing dynamic parameters
Topic Page
Saving diagnostics to a trace file 125
Displaying SQL text 129
Retaining session settings 132
Adaptive Server includes the set show_sqltext, set tracefile, and set
export_options parameters that enable you to collect diagnostic
information about poorly-running queries without having to previously
enable showplan or other investigatory parameters.
Performance and Tuning Series: Query Processing and Abstract Plans 125
Saving diagnostics to a trace file
• file_name – is the full path to the file in which you are saving the SQL text.
If you do not specify a directory path, Adaptive Server creates the file in
$SYBASE.
Note If file_name contains special characters (“:”, “/”, and so on) other
than numbers and letters, you must include file_name in quotes. For
example, this file_name must be in quotes because of the “/” for the
directory structure:
set tracefile '/tmp/mytracefile.txt' for 25
If file_name does not contain special characters and you want to save it to
$SYBASE, it does not require quotes. For example, this file_name does not
need to be in quotes:
set tracefile mytracefile.txt
• spid – server process ID whose SQL text you want saved to a trace file.
Only the users with the SA or SSO role can enable tracing for other spids.
You cannot save the SQL text for system tasks (such as the housekeeper
or the port manager).
Examples • This example opens a trace file named sql_text_file for the the current
session:
set tracefile '/var/sybase/REL1502/text_dir/sql_text_file'
Subsequent outputs from set showplan, set statistics io, and dbcc
traceon(100) are saved in sql_text_file.
• This example does not specify a directory path, so the trace file is saved in
$SYBASE/sql_text_file:
set tracefile 'sql_text_file' for 11
Any SQL run on spid 11 is saved to this tracefile.
• This example saves the SQL text for spid 86:
set tracefile
'/var/sybase/REL1502/text_dir/sql_text_file' for 86
• This example disables set tracefile:
set tracefile off
These are the restrictions for set tracefile:
• You cannot save the SQL text for system tasks (such as the housekeeper
or the port manager).
• You must have the sa or sso roles, or be granted set tracing permission, to
run enable or disable tracing.
• set tracefile is not allowed to open an existing file as a tracefile.
• During an SA or SSO session, if you enable set tracfile for a specific spid,
all subsequent tracing commands executed take effect on that spid, not the
SA or SSO spid.
• If Adaptive Server runs out of file space while writing the tracefile, it
closes the file and disables the tracing.
• If an isql session starts tracing for a spid, but the isql session quits without
disabling the tracing, another isql session can begin tracing this spid.
• Tracing occurs for the session for which it is enabled only, not for the
session that enabled it.
• You cannot trace more than one session at a time from a single sa or sso
session. If you attempt to open a tracefile for a session for which there is
already a trace file open, Adaptive Server issues this error message:
tracefile is already open for this session.
• You cannot trace the same session from multiple sa or sso sessions.
• The file storing the trace output is closed when the session being traced
quits or when you disable tracing.
• Before you allocate resources for tracing, keep in mind that each tracing
requires one file descriptor per engine.
Performance and Tuning Series: Query Processing and Abstract Plans 127
Saving diagnostics to a trace file
For example:
sp_helpapptrace
traced_spid tracer_spid trace_file
-------------- ------------- ----------
11 exited /tmp/myfile1
13 14 /tpcc/sybase.15_0/myfile2
Rebinding a trace
If a session is tracing another session, but quits without disabling the tracing,
Adaptive Server allows a new session to rebind with the earlier trace. This
means that a sa or sso is not required to finish every trace they start, but can
start a trace session, quit, and then rebind to this trace session
Performance and Tuning Series: Query Processing and Abstract Plans 129
Displaying SQL text
sp_who
2007/02/23 02:18:25.77
SQL Text: sp_who
Sproc: sp_who, Line: 0
Sproc: sp_who, Line: 20
Sproc: sp_who, Line: 22
Sproc: sp_who, Line: 25
Sproc: sp_who, Line: 27
Sproc: sp_who, Line: 30
Sproc: sp_who, Line: 55
Sproc: sp_who, Line: 64
Sproc: sp_autoformat, Line: 0
Sproc: sp_autoformat, Line: 165
Sproc: sp_autoformat, Line: 167
Sproc: sp_autoformat, Line: 177
Sproc: sp_autoformat, Line: 188
. . .
Sproc: sp_autoformat, Line: 326
Sproc: sp_autoformat, Line: 332
SQL Text: INSERT
#colinfo_af(colid,colname,usertype,type,typename,collength,maxlength,autoform
at,selected,selectorder,asname,mbyte) SELECT
c.colid,c.name,t.usertype,t.type,t.name,case when c.length < 80 then 80 else
c.length end,0,0,0,0,c.name,0 FROM tempdb.dbo.syscolumns c,tempdb.dbo.systypes
t WHERE c.id=1949946031 AND c.usertype=t.usertype
Sproc: sp_autoformat, Line: 333
Sproc: sp_autoformat, Line: 334
. . .
Sproc: sp_autoformat, Line: 535
Sproc: sp_autoformat, Line: 0
Sproc: sp_autoformat, Line: 393
Sproc: sp_autoformat, Line: 395
. . .
Sproc: sp_autoformat, Line: 686
Sproc: sp_autoformat, Line: 688
SQL Text: UPDATE #colinfo_af SET maxlength=(SELECT
isnull(max(isnull(char_length(convert(varchar(80),fid)),4)),1) FROM
#who1result ), autoformat = 1, mbyte=case when usertype in (24, 25, 34, 35) then
1 else 0 end WHERE colname='fid'
Sproc: sp_autoformat, Line: 689
Sproc: sp_autoformat, Line: 690
. . .
Sproc: sp_autoformat, Line: 815
Sproc: sp_autoformat, Line: 818
SQL Text: SELECT
fid=right(space(80)+isnull(convert(varchar(80),fid),'NULL'),3),
spid=right(space(80)+isnull(convert(varchar(80),spid),'NULL'),4),
status=SUBSTRING(convert(varchar(80),status),1,8),
loginame=SUBSTRING(convert(varchar(80),loginame),1,8),
origname=SUBSTRING(convert(varchar(80),origname),1,8),
hostname=SUBSTRING(convert(varchar(80),hostname),1,8),
blk_spid=right(space(80)+isnull(convert(varchar(80),blk_spid),'NULL'),8),
dbname=SUBSTRING(convert(varchar(80),dbname),1,6),
tempdbname=SUBSTRING(convert(varchar(80),tempdbname),1,10),
cmd=SUBSTRING(convert(varchar(80),cmd),1,17),
block_xloid=right(space(80)+isnull(convert(varchar(80),block_xloid),'NULL'),1
1) FROM #who1result order by fid, spid, dbname
Performance and Tuning Series: Query Processing and Abstract Plans 131
Retaining session settings
Performance and Tuning Series: Query Processing and Abstract Plans 133
Queries that benefit from parallel processing
Commands that return large, unsorted result sets are unlikely to benefit from
parallel processing due to network constraints. In most cases, results can be
returned from the database faster than they can be merged and returned to the
client over the network.
Parallel DMLs like insert, delete, and update are not supported and so do not
benefit from parallelism.
Enabling parallelism
To configure Adaptive Server for parallelism, enable the number of worker
processes and max parallel degree parameters.
Performance and Tuning Series: Query Processing and Abstract Plans 135
Enabling parallelism
If the query processor has insufficient worker processes, the processor tries to
adjust the query plan during runtime. If a minimal number of worker processes
are required but unavailable, the query aborts with this error message:
Insufficient number of worker processes to execute the
parallel query. Increase the value of the configuration
parameter ‘number of worker processes’
If the query processor does not find a full plan before reaching the value for
max resource granularity as a percentage of procedure cache, the search engine
continues to search until it finds the next full plan. However, if the search
engine reaches 50% of the complete procedure cache and finds no plan, it
aborts the query compilation to avoid shutting down the server.
If all tables and indexes are unpartitioned, Adaptive Server uses the value for
max repartition degree to provide the number of partitions to create as a result
of repartitioning the data. When the value is set to 1, which is the default case,
the value of max repartition degree is set to the number of online engines.
Use max repartition degree when using the force option to perform a parallel
scan on a table or index.
select * from customers (parallel)
For example, if the customers table is unpartitioned and the force option is used,
Adaptive Server tries to find the inherent partitioning degree of that table or
index, which in this case is 1. It uses the number of engines configured for the
server, or whatever degree is best based on the number of pages in the table or
index that does not exceed the value of max repartition degree.
To set max repartition degree to 5:
sp_configure "max repartition degree", 5
Performance and Tuning Series: Query Processing and Abstract Plans 137
Enabling parallelism
Performance and Tuning Series: Query Processing and Abstract Plans 139
Controlling parallelism at the session level
If you specify a value that is too large for any of the set options, the value of
the corresponding configuration parameter is used, and a message reports the
value that is in effect. While set parallel_degree, set scan_parallel_degree, set
repartition_degree, or set resource_granularity is in effect during a session, the
plans for any stored procedures that you execute are not placed in the procedure
cache. Procedures executed with these set options in effect may produce less
than optimal plans.
Performance and Tuning Series: Query Processing and Abstract Plans 141
Using parallelism selectively
See “Creating and Using Abstract Plans” on page 321 for more
information about using abstract plans.
Performance and Tuning Series: Query Processing and Abstract Plans 143
When parallel query results differ
Table 5-2 shows that the disk subsystem did not perform well after four
concurrent accesses; the performance numbers went below the acceptable limit
established by k. In general, read enough data blocks to allow for any skewed
readings.
Having established that 4 threads is optimal, provide this hint by binding it to
the object using sp_chgattribute in this way:
sp_chgattribute <tablename>, “plldegree”, 4
This tells the query optimizer to use a maximum of four threads. It may choose
fewer than four threads if it does not find enough resources. The same
mechanism can be applied to an index. For example, if an index called auth_ind
exists on authors and you want to use two threads to access it, use:
sp_chgattribute “authors.auth_ind”, “plldegree”, 4
You must run sp_chgatttribute from the current database.
Note If you need a dependable ordering of results, use order by, or run the
query in serial mode.
In addition, due to the pacing effects of multiple worker processes reading data
pages, two types of queries accessing the same data may return different results
when an aggregate or a final sort is not done. They are:
• Queries that use set rowcount
• Queries that select a column into a local variable without sufficiently
restrictive query clauses
Performance and Tuning Series: Query Processing and Abstract Plans 145
Understanding parallel query plans
Note See Chapter 2, “Using showplan,” which explains how to display a query
plan in a text-based format for each SQL statement in a batch or stored
procedure.
ROOT:EMIT Operator
|SCAN Operator
| FROM TABLE
| titles
| Table Scan.
| Forward Scan.
| Positioning at start of table.
| Using I/O Size 2 Kbytes for data pages.
| With LRU Buffer Replacement Strategy for data
pages.
As this example illustrates, the titles table is being scanned by the scan
operator, the details of which appear in the showplan output. The emit operator
reads the data from the scan operator and sends it to the client application. A
given query can create an arbitrarily complex tree of such operators.
When parallelism turned on, Adaptive Server can perform a simple scan in
parallel using the exchange operator above the scan operator. exchange
produces three worker processes (based on the three partitions), each of which
scans the three disjointed parts of the table and sends the output to the
consumer process. The emit operator at the top of the tree does not know that
the scans are done in parallel.
Example A:
select * from titles
Executed in parallel by coordinating process and 3 worker processes.
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |RESTRICT Operator
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | titles
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
Performance and Tuning Series: Query Processing and Abstract Plans 147
Adaptive Server parallel query execution model
EXCHANGE operator
The EXCHANGE operator marks the boundary between a producer and a
consumer operator (the operators below the EXCHANGE operator produce data
and those above it consume data). Example A, which showed parallel scan of
the titles table (select * from titles), the EXCHANGE: EMIT and the SCAN
operator produce data. This is shown briefly.
select * from titles
ROOT:EMIT Operator
In this example, one consumer process reads data from a pipe (which is used
as a medium to transfer data across process boundaries) and passes the data to
the emit operator, which in turn routes the result to the client. The exchange
operator also spawns worker processes, which are called producer threads. The
exchange:emit operator writes the data into a pipe managed by the exchange
operator.
Figure 5-1: Binding of thread to plan fragments in query plan
Figure 5-1 shows the process boundary between a producer and a consumer
process. There are two plan fragments in this query plan. The plan fragment
with the scan and the exchange:emit operators are cloned three ways and then
a three-to-one exchange operator writes it into a pipe. The emit operator and the
exchange operator are run by a single process, which means there is a single
clone of that plan fragment.
Pipe management
The four types of pipes managed by the exchange operator are distinguished by
how they split and merge data streams. You can determine which type of pipe
is being managed by the exchange operator by looking at its description in the
showplan output, where the number of producers and consumers are shown.
The four pipe types are described below.
Performance and Tuning Series: Query Processing and Abstract Plans 149
Adaptive Server parallel query execution model
Many-to-one In this case, the exchange operator spawns multiple producer threads and has
one consumer task that reads the data from a pipe, to which multiple producer
threads write. The exchange operator in the previous example implements a
many-to-one exchange. A many-to-one exchange operator can be
order-preserving and this technique is employed particularly when doing a
parallel sort for an order by clause and the resultant data stream merged to
generate the final ordering. The showplan output shows more than one
producer process and one consumer process.
|EXCHANGE Operator (Merged)
|Executed in parallel by 3 Producer and 1
Consumer processes
One-to-many In this case, there is one producer and multiple consumer threads. The producer
thread writes data to multiple pipes according to a partitioning scheme devised
at query optimization, and then routes data to each of these pipes. Each
consumer thread reads data from one of the assigned pipes. This kind of data
split can preserve the ordering of the data. The showplan output shows one
producer process and more than one consumer processes:
|EXCHANGE Operator (Repartitioned)
|Executed in parallel by 1 Producer
and 4 Consumer processes
Many-to-many Many-to-many means there are multiple producers and multiple consumers.
Each producer writes to multiple pipes, and each pipe has multiple consumers.
Each stream is written to a pipe. Each consumer thread reads data from one of
the assigned pipes.
|EXCHANGE Operator (Repartitioned)
|Executed in parallel by 3 Producer and 4
Consumer processes
Replicated exchange In this case, the producer thread writes all of its data to each pipe that the
operators exchange operator configures. The producer thread makes a number of copies
of the source data (the number is specified by the query optimizer) equal to the
number of pipes in the exchange operator. Each consumer thread reads data
from one of the assigned pipes. The showplan output shows this as follows:
|EXCHANGE (Replicated)
|Executed in parallel by 3 Producers and 4
Consumer processes
Performance and Tuning Series: Query Processing and Abstract Plans 151
Adaptive Server parallel query execution model
titles (3 partitions)
Each exchange operator is also associated with a server process named the beta
process, which can be either the alpha process or a worker process. The beta
process associated with a given exchange operator is the local coordinator for
the execution of the plan fragment below the exchange operator. In the example
above, the beta process is the same process as the alpha process, because the
plan to be executed has only one level of exchange operators.
Next, use this query to illustrate what happens when the query plan contains
multiple exchange operators:
select count(*),pub_id, pub_date
from titles
group by pub_id, pub_date
Performance and Tuning Series: Query Processing and Abstract Plans 153
Adaptive Server parallel query execution model
Scheduling level: 0
=======Thread to XCHg Map BEGINS=======
The query processing engine and its operators take advantage of the Adaptive
Server partitioning strategy. Partitioning defined on table and indexes is called
static partitioning. In addition, Adaptive Server dynamically repartitions data
to match the needs for relational operations like joins, vector aggregation,
distincts, unions, and so on. Repartitioning is done in streaming mode and no
storage is associated with it. Repartitioning is not the same as issuing the alter
table repartition command, where static repartitioning is done.
The following sections discuss these two classes of operators. The examples in
these sections use the following table with enough data to trigger parallel
processing.
create table RA2(a1 int, a2 int, a3 int)
Table scan
For horizontal parallelism, either at least one of the tables in the query must be
partitioned, or the configuration parameter max repartition degree must be
greater than 1. If max repartition degree is set to 1, Adaptive Server uses the
number of online engines as a hint. When Adaptive Server runs horizontal
parallelism, it runs multiple versions of one or more operators in parallel. Each
clone of an operator works on its partition, which can be statically created or
dynamically built at execution.
Serial table scan The following example below shows the serial execution of a query where the
table RA2 is scanned using the table scan operator. The result of this operation
is routed to the emit operator, which forwards the result to the client.
Performance and Tuning Series: Query Processing and Abstract Plans 155
Adaptive Server parallel query execution model
ROOT:EMIT Operator
|SCAN Operator
| FROM TABLE
| RA2
| Table Scan.
| Forward Scan.
| Positioning at start of table.
| Using I/O Size 2 Kbytes for data pages.
| With LRU Buffer Replacement Strategy for data
pages.
In versions earlier than 15.0, Adaptive Server did not try to scan an
unpartitioned table in parallel using a hash-based scan unless a force option was
used. Figure 5-4 shows a scan of an allpages-locked table executed in serial
mode by a single task T1. The task follows the page chain of the table to read
each page, while doing physical I/O if the needed pages are not in the cache.
Figure 5-4: Serial task scans data pages
Parallel table scan You can force a parallel table scan of an unpartitioned table using the Adaptive
Server force option. In this case, Adaptive Server uses a hash-based scan.
Hash-based table Figure 5-5 shows how three worker processes divide the work of accessing
scans data pages from an allpages-locked table during a hash-based table scan. Each
worker process performs a logical I/O on every page, but each process
examines rows on one-third of the pages, as indicated by differently shaded
lines. Hash-based table scans are used only if the user forces a parallel degree.
See “Partition skew” on page 198.
With one engine, the query still benefits from parallel access because one work
process can execute while others wait for I/O. If there are multiple engines,
some of the worker processes can be running simultaneously.
Figure 5-5: Multiple worker processes scans unpartitioned table
Hash-based scans increase the logical I/O for the scan, since each worker
process must access each page to hash on the page ID. For a data-only-locked
table, hash-based scans hash either on the extent ID or the allocation page ID,
so that only a single worker process scans a page and logical I/O does not
increase.
Partitioned-based If you partition this table as follows:
table scans
alter table RA2 partition by range(a1, a2)
(p1 values <= (500,100), p2 values <= (1000, 2000))
With the following query, Adaptive Server may choose a parallel scan of the
table. Parallel scan is chosen only if there are sufficient pages to scan and the
partition sizes are similar enough that the query will benefit from parallelism.
select * from RA2
Performance and Tuning Series: Query Processing and Abstract Plans 157
Adaptive Server parallel query execution model
worker processes.
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |SCAN Operator
| | | FROM TABLE
| | | RA2
| | | Table Scan.
| | | Forward Scan.
| | | Positioning at start of table.
| | | Executed in parallel with a 2-way
partition scan.
| | | Using I/O Size 2 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy
for data pages.
After partitioning the table, showplan output includes two additional operators,
exchange and exchange:emit. This query includes two worker processes, each
of which scans a given partition and passes the data to the exchange:emit
operator, as illustrated in Figure 5-1 on page 149.
Figure 5-6 shows how a query scans a table that has three partitions on three
physical disks. With a single engine, this query can benefit from parallel
processing because one worker process can execute while others sleep, waiting
for I/O or waiting for locks held by other processes to be released. If multiple
engines are available, the worker processes can run simultaneously on multiple
engines. Such a configuration can perform extremely well.
Index scan
Indexes, like tables, can be partitioned or unpartitioned. Local indexes inherit
the partitioning strategy of the table. Each local index partition scans data in
only one partition. Global indexes have a different partitioning strategy from
the base table; they reference one or more partitions.
Global nonclustered Adaptive Server supports global indexes that are nonclustered and
indexes unpartitioned for all table partitioning strategies. Global indexes are supported
for compatibility with Adaptive Server versions earlier than 15.0; they are also
useful in OLTP environments. The index and the data partitions can reside on
the same or different storage areas.
Noncovered scan of To create an unpartitioned global nonclustered index on table RA2, which is
global nonclustered partitioned by range, enter:
index using hashing
create index RA2_NC1 on RA2(a3)
This query has a predicate that uses the index key of a3:
select * from RA2 where a3 > 300
QUERY PLAN FOR STATEMENT 1 (at line 1).
. . . . . . . . . . . . . .
The type of query is SELECT.
ROOT:EMIT Operator
Performance and Tuning Series: Query Processing and Abstract Plans 159
Adaptive Server parallel query execution model
If the query does not need to access the data page, then it does not execute in
parallel. However, the partitioning columns must be added to the query;
therefore, it becomes a noncovered scan:
select a3 from RA2 where a3 > 300
Performance and Tuning Series: Query Processing and Abstract Plans 161
Adaptive Server parallel query execution model
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |SCAN Operator
| | | FROM TABLE
| | | RA2
| | | Index : RA2_NC1
| | | Forward Scan.
| | | Positioning by key.
| | | Keys are:
| | | a3 ASC
| | | Executed in parallel with a 2-way hash
scan.
| | | Using I/O Size 2 Kbytes for index leaf
pages.
| | | With LRU Buffer Replacement Strategy for
index leaf pages.
| | | Using I/O Size 2 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for
data pages.
Covered scan using If there is a nonclustered index that includes the partitioning column, there is
nonclustered global no reason for Adaptive Server to access the data pages and the query executes
index
in serial:
create index RA2_NC2 on RA2(a3,a1,a2)
ROOT:EMIT Operator
| SCAN Operator
| FROM TABLE
| RA2
| Index : RA2_NC2
| Forward Scan.
| Positioning by key.
| Index contains all needed columns. Base table
will not be read.
| Keys are:
| a3 ASC
| Using I/O Size 2 Kbytes for index leaf pages.
| With LRU Buffer Replacement Strategy for index
leaf pages.
Clustered index scans With a clustered index on an all-pages-locked table, a hash-based scan strategy
is not permitted. The only allowable strategy is a partitioned scan. Adaptive
Server uses a partitioned scan if that is necessary. For a data-only-locked table,
a clustered index is usually a placement index, which behaves as a nonclustered
index. All discussions pertaining to a nonclustered index on an all-pages-
locked table apply to a clustered index on a data-only-locked table as well.
Local indexes Adaptive Server supports clustered and nonclustered local indexes.
Clustered indexes on Local clustered indexes allow multiple threads to scan each data partition in
partitioned tables parallel, which can greatly improve performance. To take advantage of this
parallelism, use a partitioned clustered index. On a local index, data is sorted
separately within each partition. The information in each data partition
conforms to the boundaries established when the partitions were created, which
makes it possible to enforce unique index keys across the entire table.
Unique, clustered local indexes have the following restrictions:
• Index columns must include all partition columns.
• Partition columns must have the same order as the index definition's
partition key.
• Unique, clustered local indexes cannot be included on a round-robin table
with more than one partition.
Nonclustered indexes Adaptive Server supports local, nonclustered indexes on partitioned tables.
on partitioned tables
There is, however, a slight difference when using local indexes. When doing a
covered index scan of a local nonclustered index, Adaptive Server can still use
a parallel scan because the index pages are partitioned as well.
Performance and Tuning Series: Query Processing and Abstract Plans 163
Adaptive Server parallel query execution model
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |SCAN Operator
| | | FROM TABLE
| | | RA2
| | | Index : RA2_NC2L
| | | Forward Scan.
| | | Positioning by key.
| | | Index contains all needed columns. Base
table will not be read.
| | | Keys are:
| | | a3 ASC
| | | Executed in parallel with a 2-way
partition scan.
| | | Using I/O Size 2 Kbytes for index leaf
pages.
| | | With LRU Buffer Replacement Strategy
for index leaf pages.
Sometimes, Adaptive Server chooses a hash-based scan on a local index. This
occurs when a different parallel degree is needed or when the data in the
partition is skewed such that a hash-based parallel scan is preferred.
Scalar aggregation
The Transact-SQL scalar aggregation operation can be done in serial or in
parallel.
ROOT:EMIT Operator
| |
| | |EXCHANGE:EMIT Operator
| | |
| | | |SCALAR AGGREGATE Operator
| | | | Evaluate Ungrouped COUNT AGGREGATE.
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RA2
| | | | | Table Scan.
| | | | | Forward Scan.
| | | | | Positioning at start of table.
Performance and Tuning Series: Query Processing and Abstract Plans 165
Adaptive Server parallel query execution model
Serial aggregation
Adaptive Server may also choose to do the aggregation in serial. If the amount
of data to be aggregated is not enough to guarantee a performance advantage,
a serial aggregation may be the preferred technique. In case of a serial
aggregation, the result of the scan is merged using a many-to-one exchange
operator. This is shown in the example below, where a selective predicate has
been added to minimize the amount of data flowing into the scalar aggregate
operator. In such a case, it probably does not make sense to do the aggregation
in parallel.
select count(*) from RA2 where a2 = 10
QUERY PLAN FOR STATEMENT 1 (at line 1).
Executed in parallel by coordinating process and 2
worker processes.
ROOT:EMIT Operator
| |
| | |EXCHANGE:EMIT Operator
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | RA2
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Executed in parallel with a 2-way
partition scan.
| | | | Using I/O Size 2 Kbytes for data
pages.
| | | | With LRU Buffer Replacement
Strategy for data pages.
union all
union all operators are implemented using a physical operator by the same
name. union all is a fairly simple operation and should be used in parallel only
when the query is moving a lot of data.
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |UNION ALL Operator has 2 children.
| | |
| | | |SCAN Operator
| | | | FROM TABLE
Performance and Tuning Series: Query Processing and Abstract Plans 167
Adaptive Server parallel query execution model
| | | | RA2
| | | | Table Scan.
. . . . . . . . . . . . . . . . . . .
| | | | Executed in parallel with a 2-way
partition scan.
. . . . . . . . . . . . . . . . . . .
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | HA2
| | | | Table Scan.
. . . . . . . . . . . . . . . . . . .
| | | | Executed in parallel with a 2-way
partition scan.
ROOT:EMIT Operator
| |
| | |EXCHANGE:EMIT Operator
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | RA2
| | | | Table Scan.
| | | | Executed in parallel with a 2-way
partition scan.
|
| |EXCHANGE Operator (Merged)
| |Executed in parallel by 2 Producer and 1
Consumer processes.
| |
| | |EXCHANGE:EMIT Operator
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | HA2
| | | | Table Scan.
| | | | Executed in parallel with a 2-way
partition scan.
join
If two tables are joined in parallel, Adaptive Server tries to use semantics-based
partitioning to make the join more efficient, depending on the amount of data
being joined and the type of partitioning that each of the operands have. If the
amount of data to be joined is small, but the number of pages to scan for each
of the tables is quite significant, Adaptive Server serializes the parallel streams
from each side and the join is done in serial mode. In this case, the query
optimizer determines that it is suboptimal to run a join operation in parallel. In
general, one or both of the operands used for the join operators may be any
intermediate operator, like another join or a grouping operator, but the examples
used show only scans as operands.
Tables with same The partitioning of each operand of a join is useful only with respect to the join
useful partitioning predicate. If two tables have the same partitioning, and the partitioning
columns are a subset of the join predicate, the tables are said to be
equipartitioned. For example, if you create another table, RB2, which is
partitioned similarly to that of RA2, using the following command:
Performance and Tuning Series: Query Processing and Abstract Plans 169
Adaptive Server parallel query execution model
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |NESTED LOOP JOIN Operator
(Join Type: Inner Join)
| | |
| | | |RESTRICT Operator
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RB2
| | | | | Table Scan.
| | | | | Forward Scan.
| | | | | Positioning at start of table.
| | | | | Executed in parallel with a
2-way partition scan.
| | |
| | | |RESTRICT Operator
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RA2
| | | | | Table Scan.
| | | | | Forward Scan.
| | | | | Positioning at start of table.
| | | | | Executed in parallel with a
2-way partition scan.
The exchange operator is shown above the nested-loop join. This implies that
exchange spawns two producer threads: the first scans the first partition of RA2
and RB2 and performs the nested-loop join; the second scans the second
partition of RA2 and RB2 to do the nested-loop join. The two threads merge the
results using a many-to-one (in this case, two-to-one) exchange operator.
One of the tables with In this example, the table RB2 is repartitioned to a three-way hash partitioning
useful partitioning on column b1 using the alter table command.
alter table RB2 partition by hash(b1) (p1, p2, p3)
Now, take a slightly modified join query as shown below:
select * from RA2, RB2 where a1 = b1
The partitioning on table RA2 is not useful because the partitioned columns are
not a subset of the joining columns (that is, given a value for the joining column
a1, you cannot specify the partition to which it belongs). However, the
partitioning on RB2 is helpful because it matches the joining column b1 of RB2.
In this case, the query optimizer repartitions table RA2 to match the partitioning
of RB2 by using hash partitioning on column a1 of RA2 (the joining column,
which is followed by a three-way merge join). The many-to-many (two-to-
three) exchange operator above the scan of RA2 does this dynamic
repartitioning. The exchange operator above the merge join operator merges the
result using a many-to-one (three-to-one, in this case) exchange operator. The
showplan output for this query is shown in the following example:
select * from RA2, RB2 where a1 = b1
QUERY PLAN FOR STATEMENT 1 (at line 1).
Executed in parallel by coordinating process and 5
worker processes.
ROOT:EMIT Operator
|EXCHANGE Operator (Merged)
|Executed in parallel by 3 Producer and 1 Consumer
Performance and Tuning Series: Query Processing and Abstract Plans 171
Adaptive Server parallel query execution model
processes.
|
| |EXCHANGE:EMIT Operator
| |
| | |MERGE JOIN Operator (Join Type: Inner
Join)
| | | Using Worktable3 for internal storage.
| | | Key Count: 1
| | | Key Ordering: ASC
| | |
| | | |SORT Operator
| | | |Using Worktable1 for internal storage.
| | | |
| | | | |EXCHANGE Operator (Repartitioned)
| | | | |Executed in parallel by 2 Producer
and 3 Consumer processes.
| | | | |
| | | | | |EXCHANGE:EMIT Operator
| | | | | |
| | | | | | |RESTRICT Operator
| | | | | | |
| | | | | | | |SCAN Operator
| | | | | | | | FROM TABLE
| | | | | | | | RA2
| | | | | | | | Table Scan.
| | | | | | | | Forward Scan.
| | | | | | | | Positioning at start
of table.
| | | | | | | | Executed in parallel
with a 2-way
partition scan.
| | |
| | | |SORT Operator
| | | |Using Worktable2 for internal storage.
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RB2
| | | | | Table Scan.
| | | | | Forward Scan.
| | | | | Positioning at start of table.
| | | | | Executed in parallel with a
3-way partition scan.
Both tables with The next example uses a join where the native partitioning of the tables on both
useless partitioning sides is useless. The partitioning on table RA2 is on columns (a1,a2) and that
of RB2 is on (b1). The join predicate is on different sets of columns, and the
partitioning for both tables does not help at all. One option is to dynamically
repartition both sides of the join. By repartitioning table RA2 using a M-to-N
(two-to-three) exchange operator, Adaptive Server chooses column a3 of table
RA2 for repartitioning, as it is involved in the join with table RB2. For identical
reasons, table RB2 is also repartitioned three ways on column b3. The
repartitioned operands of the join are equipartitioned with respect to the join
predicate, which means that the corresponding partitions from each side will
join. In general, when repartitioning needs to be done on both sides of the join
operator, Adaptive Server employs a hash-based partitioning scheme.
select * from RA2, RB2 where a3 = b3
QUERY PLAN FOR STATEMENT 1 (at line 1).
Executed in parallel by coordinating process and 8
worker processes.
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |MERGE JOIN Operator
(Join Type: Inner Join)
| | | Using Worktable3 for internal storage.
| | | Key Count: 1
| | | Key Ordering: ASC
| | |
| | | |SORT Operator
| | | |Using Worktable1 for internal
storage.
| | | |
| | | | |EXCHANGE Operator (Repartitioned)
| | | | |Executed in parallel by 2
Producer and 3 Consumer
processes.
Performance and Tuning Series: Query Processing and Abstract Plans 173
Adaptive Server parallel query execution model
| | | | |
| | | | | |EXCHANGE:EMIT Operator
| | | | | |
| | | | | | |RESTRICT Operator
| | | | | | |
| | | | | | | |SCAN Operator
| | | | | | | | FROM TABLE
| | | | | | | | RA2
| | | | | | | | Table Scan.
| | | | | | | | Forward Scan.
| | | | | | | | Positioning at
start of table.
| | | | | | | | Executed in
parallel with
a 2-way
partition scan.
| | |
| | | |SORT Operator
| | | |Using Worktable2 for internal
storage.
| | | |
| | | | |EXCHANGE Operator (Repartitioned)
| | | | |Executed in parallel by 3
Producer and 3 Consumer
processes.
| | | | |
| | | | | |EXCHANGE:EMIT Operator
| | | | | |
| | | | | | |SCAN Operator
| | | | | | | FROM TABLE
| | | | | | | RB2
| | | | | | | Table Scan.
| | | | | | | Forward Scan.
| | | | | | | Positioning at start
of table.
| | | | | | | Executed in parallel
with a 3-way
partition scan.
In general, all joins, including nested-loop, merge, and hash joins, behave in a
similar way. nested-loop joins display one exception, which is that the inner
side of a nested-loop join cannot be repartitioned. This limitation occurs
because, in the case of a nested-loop join, a column value for the joining
predicate is pushed from the outer side to the inner side.
Replicated join A replicated join is useful when an index nested-loop join needs to be used.
Consider the case where a large table has a useful index on the joining column,
but useless partitioning, and joins to a small table that is either partitioned or
not partitioned. The small table can be replicated N ways to that of the inner
table, where N is the number of partitions of the large table. Each partition of
the large table is joined with the small table and, because no exchange operator
is needed on the inner side of the join, an index nested-loop join is allowed.
create table big_table(b1 int, b2 int, b3 int)
partition by hash(b3) (p1, p2)
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |NESTED LOOP JOIN Operator (Join Type:
Inner Join)
| | |
| | | |EXCHANGE Operator (Replicated)
| | | |Executed in parallel by 1 Producer
and 2 Consumer processes.
| | | |
| | | | |EXCHANGE:EMIT Operator
| | | | |
| | | | | |SCAN Operator
| | | | | | FROM TABLE
Performance and Tuning Series: Query Processing and Abstract Plans 175
Adaptive Server parallel query execution model
| | | | | | small_table
| | | | | | Table Scan.
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | big_table
| | | | Index : big_table_nc1
| | | | Forward Scan.
| | | | Positioning by key.
| | | | Keys are:
| | | | b1 ASC
| | | | Executed in parallel with a
2-way hash scan.
Parallel reformatting Parallel reformatting is especially useful when you are working with a
nested-loop join. Usually, reformatting refers to materializing the inner side of
a nested join into a worktable, then creating an index on the joining predicate.
With parallel queries and nested-loop join, reformatting is also helpful when
there is no useful index on the joining column or nested-loop join is the only
viable option for a query because of the server/session/query level settings.
This is an important option for Adaptive Server. The outer side may have
useful partitioning and, if not, it can be repartitioned to create that useful
partitioning. But for the inner side of a nested-loop join, any repartitioning
means that the table must be reformatted into a worktable that uses the new
partitioning strategy. The inner scan of a nested-loop join must then access the
worktable.
In this next example, partitioning for tables RA2 and RB2 is on columns (a1,
a2) and (b1, b2) respectively. The query is run with merge and hash join turned
off for the session.
select * from RA2, RB2 where a1 = b1 and a2 = b3
ROOT:EMIT Operator
| |
| | |EXCHANGE:EMIT Operator
| | |
| | | |STORE Operator
| | | | Worktable1 created, in allpages
locking mode, for REFORMATTING.
| | | | Creating clustered index.
| | | |
| | | | |INSERT Operator
| | | | | The update mode is direct.
| | | | |
| | | | | |EXCHANGE Operator
(Repartitioned)
| | | | | |Executed in parallel by
2 Producer and 4
Consumer processes.
| | | | | |
| | | | | | |EXCHANGE:EMIT Operator
| | | | | | |
| | | | | | | |RESTRICT Operator
| | | | | | | |
| | | | | | | | |SCAN Operator
| | | | | | | | | FROM TABLE
| | | | | | | | | RB2
| | | | | | | | | Table Scan.
| | | | | | | | | Executed in
parallel
with a
2-way
partition
scan.
| | | | |
| | | | | TO TABLE
| | | | | Worktable1.
|
| |EXCHANGE Operator (Merged)
| |Executed in parallel by 4 Producer
and 1 Consumer processes.
| |
| | |EXCHANGE:EMIT Operator
| | |
| | | |NESTED LOOP JOIN Operator
Performance and Tuning Series: Query Processing and Abstract Plans 177
Adaptive Server parallel query execution model
The sequence operator executes all of its child operators but the last, before
executing the last child operator. In this case, the sequence operator executes
the first child operator, which reformats table RB2 into a worktable using a
four-way hash partitioning on columns b1 and b3. The table RA2 is also
repartitioned four ways to match the stored partitioning of the worktable.
Serial join Sometimes, it may not make sense to run a join in parallel because of the
amount of data that needs to be joined. If you run a query similar to that of the
earlier join queries, but now have predicates on each of the tables (RA2 and
RB2) such that the amount of data to be joined is not enough, the join may be
done in serial mode. In such a case, it does not matter how these tables are
partitioned. The query still benefits from scanning the tables in parallel.
select * from RA2, RB2 where a1=b1 and a2 = b2
and a3 = 0 and b2 = 20
worker processes.
ROOT:EMIT Operator
| | |
| | | |EXCHANGE:EMIT Operator
| | | |
| | | | |RESTRICT Operator
| | | | |
| | | | | |SCAN Operator
| | | | | | FROM TABLE
| | | | | | RA2
| | | | | | Table Scan.
| | | | | | Executed in parallel with
a 2-way partition scan.
| |SORT Operator
| |Using Worktable2 for internal storage.
| |
| | |EXCHANGE Operator (Merged)
| | |Executed in parallel by 2 Producer and
1 Consumer processes.
| | |
| | | |EXCHANGE:EMIT Operator
| | | |
| | | | |RESTRICT Operator
| | | | |
| | | | | |SCAN Operator
| | | | | | FROM TABLE
| | | | | | RB2
Performance and Tuning Series: Query Processing and Abstract Plans 179
Adaptive Server parallel query execution model
| | | | | | Table Scan.
| | | | | | Executed in parallel with
a 2-way partition scan.
Semijoins Semijoins, which result from flattening of in/exist subqueries, behave the same
way as regular inner joins. However, replicated joins are not used for semijoins,
because an outer row can match more than one time in such a situation.
Outer joins In terms of parallel processing for outer joins, replicated joins are not
considered. Everything else behaves in a similar way as regular inner joins.
One other point of difference is that no partition elimination is done for any
table in an outer join that belongs to the outer group.
Vector aggregation
Vector aggregation refers to queries with group-bys. There are different ways
Adaptive Server can perform vector aggregation. The actual algorithms are not
described here; only the technique for parallel evaluation is shown in the
following sections.
In-partitioned vector If any base or intermediate relation requires a grouping and is partitioned on a
aggregation subset, or the same columns as that of the columns in the group by clause, the
grouping operation can be done in parallel on each of the partitions and the
resultant grouped streams merged using a simple N-to-1 exchange. This is
because a given group cannot appear in more than one stream. The same
restriction applies to grouping over any SQL query as long as you use
semantics-based partitioning on the grouping columns or a subset of them. This
method of parallel vector aggregation is called in-partitioned aggregation.
The following query uses a parallel in-partitioned vector aggregation since
range partitioning is defined on the columns a1 and a2, which also happens to
be the column on which the aggregation is needed.
select count(*), a1, a2 from RA2 group by a1,a2
QUERY PLAN FOR STATEMENT 1 (at line 1).
Executed in parallel by coordinating process and 2
worker processes.
ROOT:EMIT Operator
1 Consumer processes.
|
| |EXCHANGE:EMIT Operator
| |
| | |HASH VECTOR AGGREGATE Operator
| | | GROUP BY
| | | Evaluate Grouped COUNT AGGREGATE.
| | | Using Worktable1 for internal storage.
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | RA2
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Executed in parallel with a 2-way
partition scan.
| | | | Using I/O Size 2 Kbytes for data
pages.
| | | | With LRU Buffer Replacement
Strategy for data pages.
Repartitioned vector Sometimes, the partitioning of the table or the intermediate results may not be
aggregation useful for the grouping operation. It may still be worthwhile to do the grouping
operation in parallel by repartitioning the source data to match the grouping
columns, then applying the parallel vector aggregation. Such a scenario is
shown below, where the partitioning is on columns (a1, a2), but the query
requires a vector aggregation on column a1.
select count(*), a1 from RA2 group by a1
ROOT:EMIT Operator
Performance and Tuning Series: Query Processing and Abstract Plans 181
Adaptive Server parallel query execution model
|
| |EXCHANGE:EMIT Operator
| |
| | |HASH VECTOR AGGREGATE Operator
| | | GROUP BY
| | | Evaluate Grouped COUNT AGGREGATE.
| | | Using Worktable1 for internal storage.
| | |
| | | |EXCHANGE Operator (Repartitioned)
| | | |Executed in parallel by 2 Producer
and 2 Consumer processes.
| | | |
| | | | |EXCHANGE:EMIT Operator
| | | | |
| | | | | |SCAN Operator
| | | | | | FROM TABLE
| | | | | | RA2
| | | | | | Table Scan.
| | | | | | Forward Scan.
| | | | | | Positioning at start of
table.
| | | | | | Executed in parallel with
a 2-way partition scan.
Two-phased vector For the query in the previous example, repartitioning may be expensive.
aggregation Another possibility is to do a first level of grouping, merge the data using a
N-to-1 exchange operator, then do another level of grouping. This is called a
two-phased vector aggregation. Depending on the number of duplicates for the
grouping column, Adaptive Server can reduce the cardinality of the data
streaming through the N-to-1 exchange, which reduces the cost of the second
level of grouping.
select count(*), a1 from RA2 group by a1
ROOT:EMIT Operator
| GROUP BY
| Evaluate Grouped SUM OR AVERAGE AGGREGATE.
| Using Worktable2 for internal storage.
|
| |EXCHANGE Operator (Merged)
| |Executed in parallel by 2 Producer and
1 Consumer processes.
| |
| | |EXCHANGE:EMIT Operator
| | |
| | | |HASH VECTOR AGGREGATE Operator
| | | | GROUP BY
| | | | Evaluate Grouped COUNT AGGREGATE.
| | | | Using Worktable1 for internal
storage.
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RA2
| | | | | Table Scan.
| | | | | Executed in parallel with
a 2-way partition scan.
Serial vector As with some of the earlier examples, if the amount of data flowing into the
aggregation grouping operator is restricted by using a predicate, executing that query in
parallel may not make much sense. In such a case, the partitions are scanned in
parallel and an N-to-1 exchange operator is used to serialize the stream
followed by a serial vector aggregation:
select count(*), a1, a2 from RA2
where a1 between 100 and 200
group by a1, a2
ROOT:EMIT Operator
Performance and Tuning Series: Query Processing and Abstract Plans 183
Adaptive Server parallel query execution model
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |NESTED LOOP JOIN Operator (Join Type:
Inner Join)
| | |
| | | |SCAN Operator
| | | | FROM OR List
| | | | OR List has up to 2 rows of OR/IN
values.
| | |
| | | |RESTRICT Operator
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RA2
| | | | | Index : RA2_NC1
| | | | | Forward Scan.
| | | | | Positioning by key.
| | | | | Keys are:
| | | | | a3 ASC
| | | | | Executed in parallel with a
2-way hash scan.
Performance and Tuning Series: Query Processing and Abstract Plans 185
Adaptive Server parallel query execution model
Queries with or Adaptive Server takes a disjunctive predicate like an or clause and applies each
clauses side of the disjunction separately to qualify a set of row IDs (RIDs). The set of
conjunctive predicates on each side of the disjunction must be indexable. Also,
the conjunctive predicates on each side of the disjunction cannot have further
disjunction within them; that is, it makes little sense to use an arbitrarily deep
nesting of disjunctive and conjunctive clauses. In the next example, a
disjunctive predicate is taken on the same column (you can have predicates on
different columns as long as you have indexes that can do inexpensive scans),
but the predicates may qualify an overlapping set of data rows. Adaptive Server
uses the predicates on each side of the disjunction separately and qualifies a set
of row IDs. These row IDs are then subjected to duplicate elimination.
select a3 from RA2 where a3 = 2955 or a3 > 2990
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |RID JOIN Operator
| | | Using Worktable2 for internal storage.
| | |
| | | |HASH UNION Operator has 2 children.
| | | | Using Worktable1 for internal storage.
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RA2
| | | | | Index : RA2_NC1
| | | | | Forward Scan.
| | | | | Positioning by key.
| | | | | Index contains all needed
columns.Base table will not
be read.
| | | | | Keys are:
| | | | | a3 ASC
| | | | | Executed in parallel with a
2-way hash scan.
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RA2
| | | | | Index : RA2_NC1
| | | | | Forward Scan.
| | | | | Positioning by key.
| | | | | Index contains all needed
columns. Base table will
not be read.
| | | | | Keys are:
| | | | | a3 ASC
| | | | | Executed in parallel with a
2-way hash scan.
| | | |RESTRICT Operator
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RA2
| | | | | Using Dynamic Index.
| | | | | Forward Scan.
| | | | | Positioning by Row IDentifier
(RID.)
| | | | | Using I/O Size 2 Kbytes for
data pages.
| | | | | With LRU Buffer Replacement
Strategy for data pages.
Two separate index scans are employed using the index RA2_NC1, which is
defined on the column a3. The qualified set of row IDs are then checked for
duplicate row IDs, and finally, joined back to the base table. Note the line
Positioning by Row Identifier (RID). You can use different indexes
for each side of the disjunction, depending on what the predicates are, as long
as they are indexable. One way to easily identify this is to run the query
separately with each side of the disjunction to make sure that the predicates are
indexable. Adaptive Server may not choose an index intersection if it seems
more expensive than a single scan of the table.
Performance and Tuning Series: Query Processing and Abstract Plans 187
Adaptive Server parallel query execution model
Queries with an order If a query requires sorted output because of the presence of an order by clause,
by clause Adaptive Server can apply the sort in parallel. First, Adaptive Server tries to
avoid the sort if there is some inherent ordering available. If Adaptive Server
is forced to do the sort, it sees if the sort can be done in parallel. To do that,
Adaptive Server may repartition an existing data stream or it may use the
existing partitioning scheme, then apply the sort to each of the constituent
streams. The resultant data is merged using an N-to-1 order, preserving the
exchange operator.
select * from RA2 order by a1, a2
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |SORT Operator
| | | Using Worktable1 for internal storage.
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | RA2
| | | | Index : RA2_NC2L
| | | | Forward Scan.
| | | | Positioning at index start.
| | | | Executed in parallel with a
2-way partition scan.
Depending upon the volume of data to be sorted, and the available resources,
Adaptive Server may repartition the data stream to a higher degree than the
current degree of the stream, so that the sort operation is faster. The degrees of
sorting depends on whether the benefit obtained from doing the sort in parallel
far outweighs the overheads of repartitioning.
Subqueries
Adaptive Server uses different methods to reduce the cost of processing
subqueries. Parallel optimization depends on the type of subquery:
• Materialized subqueries – parallel query methods are not considered for
the materialization step.
• Flattened subqueries – parallel query optimization is considered only
when the subquery is flattened to a regular inner join or a semijoin.
• Nested subqueries – parallel operations are considered for the outermost
query block in a query containing a subquery; the inner, nested queries
always execute serially. This means that all tables in nested subqueries are
accessed serially. In the following example, the table RA2 is accessed in
parallel, but the result is that the table is serialized using a two-to-one
exchange operator before accessing the subquery. The table RB2 inside the
subquery is accessed in parallel.
select count(*) from RA2 where not exists
(select * from RB2 where RA2.a1 = b1)
ROOT:EMIT Operator
| | |
| | | |EXCHANGE:EMIT Operator
| | | |
| | | | |RESTRICT Operator
| | | | |
Performance and Tuning Series: Query Processing and Abstract Plans 189
Adaptive Server parallel query execution model
| | | | | |SCAN Operator
| | | | | | FROM TABLE
| | | | | | RA2
| | | | | | Index : RA2_NC2L
| | | | | | Forward Scan.
| | | | | | Executed in parallel with
a 2-way partition scan.
| |
| | Run subquery 1 (at nesting level 1).
| |
| | QUERY PLAN FOR SUBQUERY 1 (at nesting
level 1 and at line 2).
| |
| | Correlated Subquery.
| | Subquery under an EXISTS predicate.
| |
| | |SCALAR AGGREGATE Operator
| | | Evaluate Ungrouped ANY AGGREGATE.
| | | Scanning only up to the first
qualifying row.
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | RB2
| | | | Table Scan.
| | | | Forward Scan.
| |
| | END OF QUERY PLAN FOR SUBQUERY 1.
The following example shows an in subquery flattened into a semijoin.
Adaptive Server converts this into an inner join to provide greater flexibility in
shuffling the tables in the join order. As seen below, the table RB2, which was
originally in the subquery, is now being accessed in parallel.
select * from RA2 where a1 in (select b1 from RB2)
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |MERGE JOIN Operator (Join Type: Inner Join)
| | | Using Worktable3 for internal storage.
| | | Key Count: 1
| | | Key Ordering: ASC
| | |
| | | |SORT Operator
| | | | Using Worktable1 for internal
storage.
| | | |
| | | | |SCAN Operator
| | | | | FROM TABLE
| | | | | RB2
| | | | | Table Scan.
| | | | | Executed in parallel with a
3-way partition scan.
| | |
| | | |SORT Operator
| | | | Using Worktable2 for internal
storage.
| | | |
| | | | |EXCHANGE Operator (Merged)
| | | | |Executed in parallel by 2
Producer and 3 Consumer
processes.
| | | | |
| | | | | |EXCHANGE:EMIT Operator
| | | | | |
| | | | | | |RESTRICT Operator
| | | | | | |
| | | | | | | |SCAN Operator
| | | | | | | | FROM TABLE
| | | | | | | | RA2
| | | | | | | | Index : RA2_NC2L
| | | | | | | | Forward Scan.
| | | | | | | | Positioning at
index start.
| | | | | | | | Executed in
parallel with
Performance and Tuning Series: Query Processing and Abstract Plans 191
Adaptive Server parallel query execution model
a 2-way
partition scan.
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |INSERT Operator
| | | The update mode is direct.
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | RA2
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Executed in parallel with a 2-way
partition scan.
| | |
| | | TO TABLE
| | | RAT2
| | | Using I/O Size 2 Kbytes for data
pages.
Adaptive Server does not try to increase the degree of the stream coming from
the scan of table RA2, and uses it to do a parallel insert into the destination
table. The destination table is initially created using round-robin partitioning of
degree two. After the insert, the table is unpartitioned.
If the data set to be inserted is not big enough, Adaptive Server may choose to
insert this data in serial. The scan of the source table can still be done in
parallel. The destination table is then created as an unpartitioned table.
The select into allows destination partitioning to be specified. In such a case,
the destination table is created using that partitioning, and Adaptive Server
finds the most optimal way to insert data. If the destination table must be
partitioned the same way as the source data, and there is enough data to insert,
the insert operator executes in parallel.
The next example shows the same partitioning for source and destination table,
and demonstrates that Adaptive Server recognizes this scenario and chooses
not to repartition the source data.
select * into new_table
partition by range(a1, a2)
(p1 values <= (500,100), p2 values <= (1000, 2000))
from RA2
ROOT:EMIT Operator
Performance and Tuning Series: Query Processing and Abstract Plans 193
Adaptive Server parallel query execution model
|
| |EXCHANGE:EMIT Operator
| |
| | |INSERT Operator
| | | The update mode is direct.
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | RA2
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Executed in parallel with a 2-way
partition scan.
| | |
| | | TO TABLE
| | | RRA2
| | | Using I/O Size 16 Kbytes for data
pages.
If the source partitioning does not match that of the destination table, the source
data must be repartitioned. This is illustrated in the next example, where the
insert is done in parallel using two worker processes after the data is
repartitioned using a 2-to-2 exchange operator that converts the data from
range partitioning to hash partitioning.
select * into HHA2
partition by hash(a1, a2)
(p1, p2)
from RA2
ROOT:EMIT Operator
|
| |EXCHANGE:EMIT Operator
| |
| | |INSERT Operator
| | | The update mode is direct.
| | |
| | | |EXCHANGE OperatorEXCHANGE Operator (
Merged)
| | | |Executed in parallel by 2 Producer
and 2 Consumer processes.
| | | |
| | | | |EXCHANGE:EMIT Operator
| | | | |
| | | | | |SCAN Operator
| | | | | | FROM TABLE
| | | | | | RA2
| | | | | | Table Scan.
| | | | | | Forward Scan.
| | | | | | Positioning at start of table.
| | | | | | Executed in parallel with a
2-way partition scan.
| | |
| | | TO TABLE
| | | HHA2
| | | Using I/O Size 16 Kbytes for data
pages.
insert/delete/update
insert, delete, and update operations are done in serial in Adaptive Server.
However, tables other than the destination table used in the query to qualify
rows to be deleted or updated, can be accessed in parallel.
delete from RA2
where exists
(select * from RB2
where RA2.a1 = b1 and RA2.a2 = b2)
Performance and Tuning Series: Query Processing and Abstract Plans 195
Adaptive Server parallel query execution model
ROOT:EMIT Operator
|DELETE Operator
| The update mode is deferred.
|
| |NESTED LOOP JOIN Operator (Join Type: Inner Join)
| |
| | |SORT Operator
| | | Using Worktable1 for internal storage.
| | |
| | | |EXCHANGE Operator (Merged)
| | | |Executed in parallel by 3 Producer
and 1 Consumer processes.
| | | |
| | | | |EXCHANGE:EMIT Operator
| | | | |
| | | | | |RESTRICT Operator
| | | | | |
| | | | | | |SCAN Operator
| | | | | | | FROM TABLE
| | | | | | | RB2
| | | | | | | Table Scan.
| | | | | | | Forward Scan.
| | | | | | | Positioning at start of
table.
| | | | | | | Executed in parallel with
a 3-way partition scan.
| | | | | | | Using I/O Size 2 Kbytes
for data pages.
| | | | | | | With LRU Buffer Replacement
Strategy for data pages.
| |
| | |RESTRICT Operator
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | RA2
| | | | Index : RA2_NC1
| | | | Forward Scan.
| | | | Positioning by key.
| | | | Keys are:
| | | | a3 ASC
|
| TO TABLE
| RA2
| Using I/O Size 2 Kbytes for data pages.
The table RB2, which is being deleted, is scanned and deleted in serial.
However, table RA2 was scanned in parallel. The same scenario is true for
update or insert statements.
Partition elimination
One of the advantages of semantic partitioning is that the query processor may
be able to take advantage of this and be able to disqualify range, hash, and list
partitions at compile time. With hash partitions, only equality predicates can be
used, whereas for range and list partitions, equality and in-equality predicates
can be used to eliminate partitions. For example, consider table RA2 with its
semantic partitioning defined on columns a1, a2 where (p1 values <=
(500,100) and p2 values <= (1000, 2000)). If there are predicates on columns
a1 or columns a1, a2, then it would be possible to do some partition
elimination. For example, this statement does not qualify any data:
select * from RA2 where a1 > 1500
You can see this in the showplan output.
QUERY PLAN FOR STATEMENT 1 (at line 1).
................................
| | |SCAN Operator
| | | FROM TABLE
| | | RA2
| | | [ Eliminated Partitions : 1 2 ]
| | | Index : RA2_NC2L
The phrase Eliminated Partitions identifies the partition in accordance
with how it was created and assigns an ordinal number for identification. For
table RA2, the partition represented by p1 where (a1, a2) <= (500, 100) is
considered to be partition number one and p2 where (a1, a2) > (500, 100) and
<= (1000, 2000) is identified as partition number two.
Consider an equality query on a hash-partitioned table where all keys in the
hash partitioning have an equality clause. This can be shown by taking table
HA2, which is hash-partitioned two ways on columns (a1, a2). The ordinal
numbers refer to the order in which partitions are listed in the output of sp_help.
Performance and Tuning Series: Query Processing and Abstract Plans 197
Adaptive Server parallel query execution model
|SCAN Operator
| FROM TABLE
| HA2
| [ Eliminated Partitions : 1 ]
| Table Scan.
Partition skew
Partition skew plays an important part in determining whether a parallel
partitioned scan can be used. Adaptive Server partition skew is defined as the
ratio of the size of the largest partition to the average size of a partition.
Consider a table with four partitions of sizes 20, 20, 35, and 80 pages. The size
of the average partition is (20 + 20 + 35 + 85)/4 = 40 pages. The biggest
partition has 85 pages so partition skew is calculated as 85/40 = 2.125. In
partitioned scans, the cost of doing a parallel scan is as expensive as doing the
scan on the largest partition. Instead, a hash-based partition may turn out to be
fast, as each worker process may hash on a page number or an allocation unit
and scan its portion of the data. The penalty paid in terms of loss of
performance by skewed partitions is not always at the scan level, but rather as
more complex operators like several join operations are built over the data. The
margin of error increases exponentially in such cases.
Run sp_help on a table to see the partition skews:
sp_help HA2
........
name type partition_type partitions partition_keys
------ -------------------- -------------- -----------
HA2 base table hash 2 a1, a2
Partition_Conditions
--------------------
NULL
Ratio(Min/Avg)
----------- ----------- ----------- ------------------
---------
---------------------------
333 343 324 1.030030
0.972973
Alternatively, you can calculate skew by querying the systabstats system
catalog, where the number of pages in each partition is listed.
Runtime adjustment
If there are not enough worker processes available at runtime, the execution
engine attempts to reduce the number of worker processes used by the
exchange operators present in the plan:
Performance and Tuning Series: Query Processing and Abstract Plans 199
Adaptive Server parallel query execution model
By examining the occurrences of errors 11014 and 11015 in the error log, you
can determine the degree to which Adaptive Server uses adjusted query plans
instead of optimized query plans. To remove the restriction and allow runtime
adjustments, use:
set process_limit_action quiet
See set in the Reference Manual: Commands.
Using showplan
When you use showplan, Adaptive Server displays the optimized plan for a
given query before it runs the query. When the query plan involves parallel
processing, and a runtime adjustment is made, showplan displays this message,
followed by the adjusted query plan:
AN ADJUSTED QUERY PLAN IS BEING USED FOR STATEMENT 1
BECAUSE NOT ENOUGH WORKER PROCESSES ARE CURRENTLY
AVAILABLE.
Performance and Tuning Series: Query Processing and Abstract Plans 201
Adaptive Server parallel query execution model
Overview
Aggregate processing is one of the most useful operations in DBMS
environments. It summarizes large amounts of data with an aggregated
value, including:
• The minimum, maximum, sum, or average value of a column in a
specified set of rows
• The count of rows that match a condition
• Other statistical functions
In SQL, aggregate processing is performed using the aggregation
functions min(), max(), count(), sum(), and avg(), and group by and having
clauses. The SQL language implements two aggregate processing types,
vector aggregation and scalar aggregation. A select-project-join (SPJ)
query illustrates these two types of aggregate processing:
select r1, s1
from r, s
where r2 = s2
Vector aggregation In vector aggregation, the SPJ result set is grouped on the group by clause
expressions, and then the select clause aggregation functions are applied
to each group. The query produces one result row per group:
select r1, sum (s1)
from r, s
Performance and Tuning Series: Query Processing and Abstract Plans 203
Overview
where r2 = s2
group by r1
Scalar aggregation In scalar aggregation, there is no group by clause and the entire SPJ result set is
aggregated, as a single group, by the same select clause aggregate functions.
The query produces a single result row:
select sum (s1)
from r, s
where r2 = s2
Eager aggregation
Eager aggregation transforms the internal representation of queries such as
those discussed above, and processes them as if the aggregation is performed
incrementally: first locally, over each table, producing intermediate aggregate
results over smaller local subgroups, and then globally after the join, thus
combining the local aggregation results to produce the final result set.
These queries, which return the same result set over any data set, are derived
table SQL-level rewrites of the vector and scalar aggregation examples above.
They illustrate the eager aggregation transformations that Adaptive Server
performs on the internal representation of the queries.
Vector aggregation select r1, sum(sum_s1 * count_r)
from
(select
r1,r2,
count_r = count(*)
from r
group by r1,r2
)gr
,
(select
s2,
sum_s1 = sum(s1)
from s
group by s2
)gs
where r2-s2
group by r1
Scalar aggregation select sum(sum_s1 * count_r)
from
(select
r2,
count_r = count(*)
from r
group by r2
)gr
,
(select
s2,
sum_s1 = sum(s1)
from s
group by s2
)gs
where r2 = s2
Eager aggregation plans are generated and costed by the optimizer, and can be
chosen as the best plan. This form of advanced query optimization can result
in orders of magnitude of performance gain, relative to the original SQL query.
Performance and Tuning Series: Query Processing and Abstract Plans 205
Aggregation and query processing
MergeJoin: r2=s2
The two index scans, on r(r2) and s(s2) provide the orderings needed by the
“r2=s2” merge join. Hash-based grouping is done over the join, as the query
specifies it.
The optimizer also generates query plans that perform eager aggregation, also
called the push-down of grouping, early grouping, or eager grouping. The SQL
representation of the transform using derived tables is:
select r1, sum(sum_s1 * cnt_r)
from
(select r1, r2, cnt_r = count(*)
from r
group by r1, r2
) as gr
,
(select s2, sum_s1 = sum(s1)
from s
group by s2
) as gs
where r2 = s2
group by r1
Performance and Tuning Series: Query Processing and Abstract Plans 207
Examples
MergeJoin: r2=s2
Examples
Online data archiving The most compelling reason to implement eager aggregation is online data
archiving, which is a distributed query processing (DQP) installation where
recent OLTP read-write data is on an Adaptive Server and historical read-only
data is on another server, either Adaptive Server or ASIQ.
The following view, v, offers decision support system (DSS) applications
transparent access to local Adaptive Server data in ase_tab and, through the
Component Integration Services (CIS) proxy_asiq_tab, to remote historical
data on an ASIQ server.
group:t1;sum(v1)
join:t2=v2
t union
ase_tab proxy_aseq_tab
As this tree uses a CIS proxy table, the CIS layer uses a specialized remote scan
operator to generate and ship a plan fragment to the remote site.
Performance and Tuning Series: Query Processing and Abstract Plans 209
Examples
ASIQ
GROUP "select
q1, q2
ASE from
asiq_tab"
JOIN
scan: t union
scan: scan:
ase_tab proxy_asiq_t
scan:
asiq_tab
ASIQ
Scan: ase tab Scan: proxy_asiq_tab
Performance and Tuning Series: Query Processing and Abstract Plans 211
Examples
Scan: t union
group
ASIQ
Scan: ase tab
Scan: proxy_asiq_tab
In this example, there are two operators between the group and the CIS proxy:
a join and a union. The next transform pushes grouping below the join and the
union, achieving eager aggregation:
group
Scan: t union
group group
Grouping is now adjacent to the CIS proxy. The CIS layer can now send a
grouped query to ASIQ and return aggregated data.
Performance and Tuning Series: Query Processing and Abstract Plans 213
Examples
Performance and Tuning Series: Query Processing and Abstract Plans 215
Using eager aggregation
| | | | | Index contains all needed columns. Base table will not be read.
| | | | | Using I/O Size 2 Kbytes for index leaf pages.
| | | | | With LRU Buffer Replacement Strategy for index leaf pages.
r1
----------- -----------
1 2
2 4
(2 rows affected)
As the query performs vector aggregation over the join of r and s, the hash
vector aggregate operator at the top of the query tree is expected in all cases.
However, the group sorted operators over the scans of r and of s are not part of
the query; they perform the eager aggregation.
When advanced_aggregation is off, the plan does not contain the eager
aggregation operators group sorted:
1> set advanced_aggregation off
2> go
1> select r1, sum(s1)
2> from r, s
3> where r2=s2
4> group by r1
5> go
Performance and Tuning Series: Query Processing and Abstract Plans 217
Using eager aggregation
10> )
11> (t_scan s)
12> )
13> )"
14> go
Performance and Tuning Series: Query Processing and Abstract Plans 219
Using eager aggregation
----------- -----------
1 2
2 4
(2 rows affected)
The hash vector aggregate operator eagerly aggregates the scan of r, as
requested by the abstract plan.
This chapter describes query processing options that affect the query
processor’s choice of join order, index, I/O size, and cache strategy.
Topic Page
Special optimizing techniques 221
Specifying query processor choices 231
Specifying table order in joins 232
Specifying the number of tables considered by the query processor 233
Specifying query index 234
Specifying I/O size in a query 236
Specifying cache strategy 239
Controlling large I/O and cache strategies 241
Asynchronous log service 241
Enabling and disabling merge joins 244
Enabling and disabling join transitive closure 245
Controlling literal parameterization 246
Suggesting a degree of parallelism for a query 248
Concurrency optimization for small tables 258
Performance and Tuning Series: Query Processing and Abstract Plans 221
Viewing current optimizer settings
• set forceplan
• set [optCriteria]
• set metrics_capture
• set prefetch
• set process_limit_action
sp_options queries the sysoptions fake table, which stores information about
each set option, its category, and its current and default settings. sysoptions also
contains a bitmap that provides detailed information for each option
The syntax for sp_options is:
sp_options [ [show | help
[, option_name | category_name |null
[, dflt | non_dflt | null
[, spid] ] ] ] ]
where:
• show – lists the current and default values of all options, grouped
according to their category. Issuing sp_options show with an option name
specified shows you the current and default value for the individual option.
You can also specify a session ID, and whether you want to view options
with default settings or options with nondefault settings.
• help – show usage information. Achieve the same result by issuing
sp_options with no parameters.
• null – indicates the option for which you want to view the settings.
• dflt | non_dflt | null – indicates whether to show options with default settings
or to show options with non-default settings.
• spid – specifies the session ID. Use the session ID to view other session
settings.
For example, to display the current optimizer settings shown below, enter:
sp_options show
Category: Query Tuning
name
currentsetting defaultsetting scope
--------------------------------------------------------------------
------------------ --------------------- ---------
optlevel
ase_default ase_current 3
optgoal
allrows_mix allrows_mix 3
opttimeoutlimit
10 10 2
repartition_degree
Performance and Tuning Series: Query Processing and Abstract Plans 223
Viewing current optimizer settings
1 1 2
scan_parallel_degree
0 1 2
resource_granularity
10 10 2
. . .
Performance and Tuning Series: Query Processing and Abstract Plans 225
Viewing current optimizer settings
These optimization level criteria are enabled when you enable ase1503esd2:
Table 7-4 lists optimization criteria that are enabled when you enable
ase1503esd3 or ase_current
Set the optimization and criteria levels using one of these methods:
• At the session level – use set plan optlevel to set the optimization level for
the current session. This configures the session to use the all optimization
changes up through the current version of Adaptive Server:
set plan optlevel ase_current
Performance and Tuning Series: Query Processing and Abstract Plans 227
Viewing current optimizer settings
• For individual logins – use sp_modifylogin to set the optimization level for
logins. sp_modifylogin calls a user-created stored procedure the defines the
optimization level. For example, if you create this stored procedure to
enable the ase1503esd2 optimization level, but disable the optimization
level for cr545180:
create proc login_proc
as
set plan optlevel ase1503esd2
set cr545180 off
go
You may apply these optimization settings to any login. This applies the
settings from login_proc to user joe:
sp_modifylogin joe, 'login script', login_proc
• Across the server – use the sp_configure “optimizer level” parameter to set
the optimization level globally. This sets enables all optimizer changes up
to the current version of Adaptive Server:
sp_configure 'optimizer level', 0, 'ase_current'
• Within an abstract plan – use the optlevel abstract plan to set the optimizer
level. This example enables all optimizer changes up to the current version
of Adaptive Server for the current plan:
select name from sysdatabases
plan '(use optlevel ase_current)'
• During a session with the set command – use the set command to change
the optimizer criteria level for the current session. Optimizer criteria
changes may improve the performance for some queries while
deteriorating others. You may find better performance by applying the
optimizer criteria level at a fine grain level (perhaps at the query level).
Adaptive Server denotes some optimizer criteria levels by their CR
numbers, while other more recent optimizer changes are specified with
descriptive names. Use sp_options to view the list of available options in
the current release. This enables the optimization criteria for CR 545180:
set CR545180 on
Performance and Tuning Series: Query Processing and Abstract Plans 229
Optimizer Diagnostic Utility
2 If Adaptive Server is unnamed, set the server name then restart Adaptive
Server:
sp_addserver server_name, local
3 Any login that runs sp_opt_querystats must have the js_user_role role, and
must use a non-Null password to log in to Adaptive Server.
sp_opt_querystats require the sa_role:
Running sp_opt_querystats
Log in to Adaptive Server using the login that has the js_user_role. Run
sp_opt_querystats to analyze a query (the diagnostic information begins with
the phrase [BEGIN QUERY ANALYSIS] and ends with [END QUERY
ANALYSIS]).
au_id = "172-32-1176"
See the Reference Manual: Procedures.
Warning! Use the options described in this chapter with caution. Forced query
plans may be inappropriate in some situations and may cause poor
performance. If you include these options in your applications, regularly check
query plans, I/O statistics, and other performance data.
These options are generally intended for use as tools for tuning and
experimentation, not as long-term solutions to optimization problems.
Performance and Tuning Series: Query Processing and Abstract Plans 231
Specifying table order in joins
• Check the showplan output to determine whether index keys are used as
expected.
• Use set option show normal to look for other optimization problems.
• Run update statistics on the index.
• Use update statistics to add statistics for search arguments on unindexed
search clauses in the query, especially for search arguments that match
minor keys in compound indexes.
• Use set option show_missing_stats on to look for columns that may need
statistics.
• If the query joins more than four tables, use set table count, which may
result in an improved join order.
See “Specifying the number of tables considered by the query processor”
on page 233.
Performance and Tuning Series: Query Processing and Abstract Plans 233
Specifying query index
• If you think that a more optimal join order can shorten total query
optimization and execution time, especially for stored procedures that you
expect to be executed many times once a plan is in the procedure cache
• When saving abstract plans for later use
Use statistics time to check parse and compile time, and statistics io to verify that
the improved join order is reducing physical and logical I/O.
If increasing the table count produces an improvement in join optimization, but
unacceptably increases CPU time, rewrite the from clause in the query,
specifying the tables in the join order indicated by showplan output, and use
forceplan to run the query. Be sure that your routine performance maintenance
checks include verifying that the join order you are forcing still improves
performance.
delete table_name
from table_name [correlation_name]
(index {index_name | table_name }) ...
Performance and Tuning Series: Query Processing and Abstract Plans 235
Specifying I/O size in a query
• The index must exist at the time the query using it is optimized. You
cannot create an index and then use it in a query in the same batch.
Before specifying an index in queries:
• Check showplan output for the “Keys are” message to be sure that the
index keys are being used as expected.
• Use dbcc traceon(3604) or set option show normal to look for other
optimization problems.
• Run update statistics on the index.
• If the index is a composite index, run update statistics on the minor keys in
the index, if they are used as search arguments. This can greatly improve
query processor cost estimates. Creating statistics for other columns
frequently used for search clauses can also improve estimates.
• Use set option show_missing_stats on to look for columns that may need
statistics.
Note Reference to large I/Os are on a 2K logical page size server. If you have
an 8K page size server, the basic unit for the I/O is 8K. If you have a 16K page
size server, the basic unit for the I/O is 16K.
Performance and Tuning Series: Query Processing and Abstract Plans 237
Specifying I/O size in a query
showplan reports the I/O size used for both data and leaf-level pages.
See the Performance and Tuning Series: Monitoring Adaptive Server with
sp_sysmon.
setting prefetch
By default, a query uses large I/O whenever a large I/O pool is configured and
the query processor determines that large I/O would reduce the query cost. To
disable large I/O during a session, use:
set prefetch off
To reenable large I/O, use:
set prefetch on
If large I/O is turned off for an object using sp_cachestrategy, set prefetch on
does not override that setting.
If large I/O is turned off for a session using set prefetch off, you cannot override
the setting by specifying a prefetch size as part of a select, delete, or insert
statement.
The set prefetch command takes effect in the same batch in which it is run, so
you can include it in a stored procedure to affect the execution of the queries in
the procedure.
Performance and Tuning Series: Query Processing and Abstract Plans 239
Specifying cache strategy
showplan output shows the cache strategy used for each object, including
worktables.
Performance and Tuning Series: Query Processing and Abstract Plans 241
Asynchronous log service
You cannot use ALS if you have fewer than four engines; if you attempt to do
so, with fewer than 4 online engines an error message appears.
You can enable, disable, or configure ALS using the sp_dboption stored
procedure:
sp_dboption <db Name>, "async log service",
"true|false"
After issuing sp_dboption, you must issue a checkpoint in the database for
which you are setting the ALS option:
sp_dboption "mydb", "async log service", "true"
use mydb
checkpoint
You can use the checkpoint to identify the one or more databases or use an all
clause:
checkpoint [all | [dbname[, dbname[, dbname.....]]]
To disable ALS, enter:
sp_dboption "mydb", "async log service", "false"
use mydb
checkpoint
-------------
Before you disable ALS, make sure there are no active users in the database. If
there are active users in the database when you disable ALS, you see this error
message:
Error 3647: Cannot put database in single-user mode.
Wait until all users have logged out of the database and
issue a CHECKPOINT to disable "async log service".
Use sp_helpdb to see whether ALS is enabled in a specified database:
sp_helpdb "mydb"
----------
mydb 3.0 MB sa 2
July 09, 2002
select into/bulkcopy/pllsort, trunc log on chkpt,
async log service
For more information on these stored procedures, see the Adaptive Server
Reference Manual: Procedures.
Performance and Tuning Series: Query Processing and Abstract Plans 243
Enabling and disabling merge joins
Note Use ALS only when you identify a single database with high transaction
requirements, since setting ALS for multiple databases may cause unexpected
variations in throughput and response times. To configure ALS on multiple
databases, first check that throughput and response times are satisfactory.
The command set merge_join on overrides the server level to allow use of
merge joins in a session or stored procedure.
To enable merge joins, use:
set merge_join on
To disable merge joins, use:
set merge_join off
Performance and Tuning Series: Query Processing and Abstract Plans 245
Controlling literal parameterization
For queries that execute quickly, even when several tables are involved, join
transitive closure may increase optimization time with little improvement in
execution cost. For example, with join transitive closure applied to this query,
the number of possible joins is multiplied for each added table:
select * from t1, t2, t3, t4, ... tN
where t1.c1 = t2.c1
and t1.c1 = t3.c1
and t1.c1 = t4.c1
...
and t1.c1 = tN.c1
For joins on very large tables, however, the additional optimization time
involved in costing the join orders added by join transitive closure may result in
a join order that greatly improves the response time.
Use set statistics time to see how long Adaptive Server takes to optimize the
query. If running queries with set jtc on greatly increases optimization time, but
also improves query execution by choosing a better join order, check the
showplan, set option show_search_engine normal, or set option show_search_engine
long output. Explicitly add the useful join orders to the query text. Run the
query without join transitive closure, and get the improved execution time,
without the increased optimization time of examining all possible join orders
generated by join transitive closure.
You can also enable join transitive closure and save abstract plans for queries
that benefit. If you then execute those queries with loading from the saved
plans enabled, the saved execution plan is used to optimize the query, making
optimization time extremely short.
See Performance and Tuning: Optimizer and Abstact Plans for more
information on using abstract plans and configuring join transitive closure
server-wide.
Performance and Tuning Series: Query Processing and Abstract Plans 247
Suggesting a degree of parallelism for a query
select...
[from {tablename}
[(index index_name
[parallel [degree_of_parallelism | 1]]
[prefetch size] [lru|mru])],
{tablename} [([index_name]
[parallel [degree_of_parallelism | 1]
[prefetch size] [lru|mru])] ...
Table 7-7 shows how to combine the index and parallel keywords to obtain
serial or parallel scans.
Table 7-7: Optimizer hints for serial and parallel execution
Use: To specify a:
(index tablename parallel N) Parallel partition scan
(index index_name parallel N) Parallel index scan
(index tablename parallel 1) Serial table scan
(index index_name parallel 1) Serial index scan
(parallel N) Parallel scan, with the choice of table or index scan left to the optimizer
(parallel 1) Serial scan, with the choice of table or index scan left to the optimizer
When you specify the parallel degree for a table in a merge join, it affects the
degree of parallelism used for both the scan of the table and the merge join.
You cannot use the parallel option if you have disabled parallel processing
either at the session level with the set parallel_degree 1 command, or at the
server level with the parallel degree configuration parameter. The parallel
option cannot override these settings.
If you specify a degree_of_parallelism that is greater than the maximum
configured degree of parallelism, Adaptive Server ignores the hint.
The optimizer ignores hints that specify a parallel degree if any of the
following conditions is true:
• The from clause is used in the definition of a cursor.
• parallel is used in the from clause of an inner query block of a subquery, and
the optimizer does not move the table to the outermost query block during
subquery flattening.
• The table is a view, a system table, or a virtual table.
• The table is the inner table of an outer join.
• The query specifies exists, min, or max on the table.
Performance and Tuning Series: Query Processing and Abstract Plans 249
Optimization goals
• The value for the max scan parallel degree configuration parameter is set
to 1.
• An unpartitioned clustered index is specified or is the only parallel option.
• A nonclustered index is covered.
• The query is processed using the OR strategy.
• The select statement is used for an update or insert.
Optimization goals
Adaptive Server lets you choose a query optimization goal that best suits your
query environment:
• fastfirstrow – optimizes queries so that Adaptive Server returns the first few
rows as quickly as possible.
• allrows_oltp – optimizes queries so that Adaptive Server uses a limited
number of optimization criteria (described in “Optimization criteria” on
page 252) to find a good query plan. allrows_oltp is most useful for purely
OLTP queries.
• allrows_mixed – optimizes queries so that Adaptive Server uses most
available optimization techniques, including merge_join and parallel, to
find the best query plan. allrows_mixed, which is the default strategy, is
most useful in a mixed-query environment.
Performance and Tuning Series: Query Processing and Abstract Plans 251
Optimization criteria
Optimization criteria
You can set specific optimization criteria for each session. The optimization
criteria represent specific algorithms or relational techniques that may or may
not be considered when Adaptive Server creates a query plan. By setting
individual optimization criteria on or off, you can fine-tune the query plan for
the current session.
Note Each optimization goal has default settings for each optimization
criterion. Resetting optimization criteria may interfere with the default settings
of the current optimization goal and produce an error message—although
Adaptive Server applies the new setting.
Sybase recommends that you set individual optimization criteria only rarely
and with caution if you must fine-tune a particular query. Overriding
optimization goal settings can overly complicate query administration. Always
set optimization criteria after setting any existing session level optgoal setting;
an explicit optgoal setting may return an optimization criteria to its default
value.
Setting optimization Use the set command to enable or disable individual criteria.
criteria
For example, to enable the hash join algorithm, enter:
set hash_join 1
To disable the hash join algorithm, enter:
set hash_join 0
To enable one option and disable another, enter:
set hash_join 1, merge_join 0
Criteria descriptions Most criteria described here decides whether a particular query engine operator
can be used in the final plan chosen by the optimizer.
The optimization criteria are:
• hash_join – determines whether the query processor may use the hash join
algorithm. Hash joins may consume more runtime resources, but are
valuable when the joining columns do not have useful indexes or when a
relatively large number of rows satisfy the join condition, compared to the
product of the number of rows in the joined tables.
• hash_union_distinct – determines whether the query processor may use the
hash union distinct algorithm, which is not efficient if most rows are
distinct.
• merge_join – determines whether the query processor may use the merge
join algorithm, which relies on ordered input. merge_join is most valuable
when input is ordered on the merge key—for example, from an index scan.
merge_join is less valuable if sort operators are required to order input.
• nl_join – determines whether the query processor may use the nested-loop-
join algorithm.
• opportunistic_distinct_view – determines whether the query processor may
use a more flexible algorithm when enforcing distinctness.
• parallel_query – determines whether the query processor may use parallel
query optimization.
• store_index – determines whether the query processor may use
reformatting, which may increase the use of worktables.
• append_union_all – determines whether the query processor may use the
append union all algorithm.
Performance and Tuning Series: Query Processing and Abstract Plans 253
Optimization criteria
The query processor can also reenable nl_join for semantic reasons: for
example, if the joining tables are not connected through equijoins.
Default optimization Each optimization goal— fastfirstrow, allrows_oltp, allrows_mixed,
criteria allrows_dss—has a default setting (on (1)or off (0)) for each optimization
criterion. For example, the default setting for merge_join is off (0) for
fastfirstrow and allrows_oltp, and on (1) for allrows_mixed and allrows_dss. See
Table 7-8 for a list of default settings for each optimization criteria.
Sybase recommends that you reset the optimization goal and evaluate
performance before changing optimization criteria. Change optimization
criteria only if you must fine-tune a particular query.
Performance and Tuning Series: Query Processing and Abstract Plans 255
Controlling parallel optimization
With the exception of number of worker processes, each of these parameters can
be set at the server and the session level. To view the current session-level
value of a parameter, use the select command. For example, to view the current
value of max resource granularity, enter:
select @@resource_granularity
Note When set or viewed at the session level, these parameters do not include
“max.”
Performance and Tuning Series: Query Processing and Abstract Plans 257
Concurrency optimization for small tables
set resource_granularity 35
The value of this parameter can affect the query optimizer’s choice of operators
for a query. If max resource granularity is set low, many hash- and sort-based
operators cannot be chosen. max resource granularity also affects the scheduling
algorithm.
Note The value of max repartition degree is a suggestion only; the query
processor decides the optimal number.
max repartition degree is most useful when the tables being queried are not
partitioned, but partitioning the resultant data stream may improve
performance by allowing concurrent SQL operations.
For example, to set max repartition degree to 15 at the server level, enter:
sp_configure “max repartition degree”, 15
To set max repartition degree to 15 at the session level, enter:
set repartition_degree 15
The value of max repartition degree cannot exceed the current value of max
parallel degree. Sybase recommends that you set the value of this parameter
equal to or less than the number of CPUs or disk systems that can work in
parallel.
If concurrency on small tables is not an issue, and you want to optimize the I/O
instead, use sp_chgattribute to disable this optimization. For example, to turn
off concurrency optimization for a table:
sp_chgattribute tiny_lookup_table,
“concurrency_opt_threshold”, 0
With concurrency optimization disabled, the query processor can choose table
scans when they require fewer I/Os.
You can also increase the concurrency optimization threshold for a table. This
command sets the concurrency optimization threshold for a table to 30 pages:
sp_chgattribute lookup_table,
“concurrency_opt_threshold”, 30
The maximum value for the concurrency optimization threshold is 32,767.
Setting the value to -1 enforces concurrency optimization for a table of any
size; this setting may be useful when a table scan is chosen over indexed
access, and the resulting locking results in increased contention or deadlocks.
The current setting is stored in systabstats.conopt_thld and is printed as part of
optdiag output.
Performance and Tuning Series: Query Processing and Abstract Plans 259
Concurrency optimization for small tables
Definition
A cursor is a symbolic name that is associated with a select statement. It
enables you to access the results of a select statement one row at a time.
Figure 8-1 shows a cursor accessing the authors table.
Performance and Tuning Series: Query Processing and Abstract Plans 261
Definition
Result set
Cursor with select * from
authors where state = ’KY’ A978606525 Marcello Duncan KY
A1525070956Porczyk Howard KY
Programming can:
- Examine a row A913907285 Bier Lane KY
- Take an action based on row
values
You can think of a cursor as a “handle” on the result set of a select statement.
It enables you to examine and possibly manipulate one row at a time.
Open cursor
Fetch row
Process row
(Examine/Update/Delete)
No
Close cursor
Deallocate cursor
Example
Here is a simple example of a cursor with the “Process Rows” step shown
above in pseudocode:
declare biz_book cursor
for select * from titles
where type = ’business’
go
open biz_book
go
fetch biz_book
go
/* Look at each row in turn and perform
** various tasks based on values,
Performance and Tuning Series: Query Processing and Abstract Plans 263
Resources required at each stage
Declare cursor
Open cursor
Fetch row
Process row
(Examine/Update/Delet Table
Row locks
or (intent); Memory
Yes Get next page some
row? locks row or
page
No
locks
Close cursor
Deallocate cursor
The memory resource descriptions in Figure 8-3 and Table 8-1 refer to ad hoc
cursors for queries sent by isql or Client-Library™. For other kinds of cursors,
the locks are the same, but the memory allocation and deallocation differ
somewhat depending on the type of cursor being used, as described in
“Memory use and execute cursors” on page 266.
Table 8-1: Locks and memory use for isql and Client-Library client
cursors
Cursor
command Resource use
declare cursor When a cursor is declared, Adaptive Server uses only enough memory to store the query text.
Performance and Tuning Series: Query Processing and Abstract Plans 265
Cursor modes
Cursor
command Resource use
open When a cursor is opened, Adaptive Server allocates memory to the cursor and to store the query
plan that is generated. The server optimizes the query, traverses indexes, and sets up memory
variables. The server does not access rows yet, unless it needs to build worktables. However, it
does set up the required table-level locks (intent locks). Row and page locking behavior depends
on the isolation level, server configuration, and query type.
See the Performance and Tuning Series: Locking and Concurrency Control for more
information.
fetch When a fetch is executed, Adaptive Server gets the row(s) required and reads specified values
into the cursor variables or sends the row to the client. If the cursor needs to hold lock on rows
or pages, the locks are held until a fetch moves the cursor off the row or page or until the cursor
is closed. The lock is either a shared or an update lock, depending on how the cursor is written.
close When a cursor is closed, Adaptive Server releases the locks and some of the memory allocation.
You can open the cursor again, if necessary.
deallocate cursor When a cursor is deallocated, Adaptive Server releases the rest of the memory resources used
by the cursor. To reuse the cursor, declare it again.
Cursor modes
There are two cursor modes: read-only and update. As the names suggest, read-
only cursors can only display data from a select statement; update cursors can
be used to perform positioned updates and deletes.
Read-only mode uses shared page or row locks. If read committed with lock is
set to 0, and the query runs at isolation level 1, it uses instant duration locks,
and does not hold the page or row locks until the next fetch.
Read-only mode is in effect when you specify for read only or when the cursor’s
select statement uses distinct, group by, union, or aggregate functions, and in
some cases, an order by clause.
Update mode uses update page or row locks. It is in effect when:
• You specify for update.
• The select statement does not include distinct, group by, union, a subquery,
aggregate functions, or the at isolation read uncommitted clause.
• You specify shared.
If column_name_list is specified, only those columns are updatable.
See the Performance and Tuning Series: Locking and Concurrency Control for
more information.
Specify the cursor mode when you declare the cursor. If the select statement
includes certain options, the cursor is not updatable even if you declare it for
update.
Allpages-locked tables
For read-only cursors, queries at isolation level 0 (dirty reads) require a unique
index. Read-only cursors at isolation level 1 or 3 should produce the same
query plan as the select statement outside of a cursor.
The index requirements for updatable cursors mean that updatable cursors may
use different query plans than read-only cursors. Updatable cursors have these
indexing requirements:
• If the cursor is not declared for update, a unique index is preferred over a
table scan or a nonunique index.
Performance and Tuning Series: Query Processing and Abstract Plans 267
Index use and requirements for cursors
• If the cursor is declared for update without a for update of list, a unique
index is required on allpages-locked tables. An error is raised if no unique
index exists.
• If the cursor is declared for update with a for update of list, then only a
unique index without any columns from the list can be chosen on an
allpages-locked table. An error is raised if no unique index qualifies.
When cursors are involved, an index that contains an IDENTITY column is
considered unique, even if the index is not declared unique. In some cases,
IDENTITY columns must be added to indexes to make them unique, or the
optimizer might be forced to choose a suboptimal query plan for a cursor query.
Data-only-locked tables
In data-only-locked tables, fixed row IDs are used to position cursor scans, so
unique indexes are not required for dirty reads or updatable cursors. The only
cause for different query plans in updatable cursors is that table scans are used
if columns from only useful indexes are included in the for update of list.
Performance and Tuning Series: Query Processing and Abstract Plans 269
Comparing performance with and without cursors
return
return
end
Results from tests like these can vary widely. They are most pronounced on
systems that have busy networks, a large number of active database users, and
multiple users accessing the same table.
Performance and Tuning Series: Query Processing and Abstract Plans 271
Locking with read-only cursors
If you issue another fetch command after the last row of the result set has been
fetched, the locks on the last page are released, so there will be no cursor-
related locks.
With a data-only-locked table:
• If the cursor query runs at isolation level 1, and read committed with lock is
set to 0, you do not see any page or row locks. The values are copied from
the page or row, and the lock is immediately released.
• If read committed with lock is set to 1 or if the query runs at isolation level
2 or 3, you see either shared page or shared row locks at the point that
Table 8-3 indicates shared page locks. If the table uses datarows locking,
the sp_lock report includes the row ID of the fetched row.
Performance and Tuning Series: Query Processing and Abstract Plans 273
Partitioned heap tables and cursors
Note If cursor operations require all inserts to be made at the end of a single
page chain, do not partition the table used in the cursor scan.
• Fetch more than one row if you are returning rows to the client.
• Keep cursors open across commits and rollbacks.
• Open multiple cursors on a single connection.
Performance and Tuning Series: Query Processing and Abstract Plans 275
Optimizing tips for cursors
In this table, the additional locks, or more restrictive locks for the two versions
of the for update clause are emphasized.
Table 8-4: Effects of for update clause and shared on cursor locking
Clause titles authors titleauthor
None sh_page on index
sh_page on data sh_page on data sh_page on data
for update updpage on index updpage on index
updpage on data updpage on data updpage on data
for update of sh_page on index
price updpage on data sh_page on data sh_page on data
for update of sh_page on index
price sh_page on data sh_page on data sh_page on data
+ shared
Performance and Tuning Series: Query Processing and Abstract Plans 277
Optimizing tips for cursors
If you choose to keep transactions short, closing and opening the cursor can
affect throughput, since Adaptive Server needs to rematerialize the result set
each time the cursor is opened. If you choose to perform more work in each
transaction, this can cause concurrency problems, since the query holds locks.
Topic Page
Overview 279
Executing QP metrics 280
Accessing metrics 280
Using metrics 282
Clearing metrics 284
Restricting query metrics capture 285
Understanding the UID in sysquerymetrics 286
Overview
Query processing (QP) metrics identify and compare empirical metric
values in query execution. When a query is executed, it is associated with
a set of defined metrics that are the basis for comparison in QP metrics.
Captured metrics include:
• CPU execution time – the time, in milliseconds, it takes to execute the
query.
• Elapsed time – the time, in milliseconds, from after the compile to the
end of the execution.
• Logical I/O – the number of logical I/O reads.
• Physical I/O – the number of physical I/O reads.
• Count – the number of times a query is executed.
• Abort count – the number of times a query is aborted by the resource
governor due to a resource limit being exceeded.
Each metric, except count and abort count, has three values: minimum,
maximum, and average.
Performance and Tuning Series: Query Processing and Abstract Plans 279
Executing QP metrics
Executing QP metrics
You can activate and use QP metrics at the server level or at the session level.
At the server level, use sp_configure with the enable metrics capture option. The
QP metrics for ad hoc statements are captured directly into a system catalog,
while the QP metrics for statements in a stored procedure are saved in a
procedure cache. When the stored procedure or query in the statement cache is
flushed, the respective captured metrics are written to the system catalog.
sp_configure "enable metrics capture", 1
At a session level, use set metrics_capture on/off:
set metrics_capture on/off
Accessing metrics
QP metrics are always captured in the default group, which is group 1 in each
respective database. Use sp_metrics ‘backup’ to move saved QP metrics from
the default running group to a backup group. Access metric information using
a select statement with order by against the sysquerymetrics view. See
“sysquerymetrics view” on page 280 for details.
You can also use a data manipulation language (DML) statement to sort the
metric information and identify the specific queries for evaluation. See Chapter
2, “Understand Component Integration Services,” in the Component
Integration Services Users Guide, which is part of the Adaptive Server
Enterprise documentation set. .
sysquerymetrics view
Field Definition
uid User ID
gid Group ID
id Unique ID
hashkey Hash key over the SQL query text
sequence Sequence number for a row when multiple rows are required for the text of the SQL code
exec_min Minimum execution time
Field Definition
exec_max Maximum execution time
exec_avg Average execution time
elap_min Minimum elapsed time
elap_max Maximum elapsed time
elap_avg Average elapsed time
lio_min Minimum logical I/O
lio_max Maximum logical I/O
lio_avg Average logical I/O
pio_min Minimum physical I/O
pio_max Maximum physical I/O
pio_avg Average physical I/O
cnt Number of times the query has been executed
abort_cnt Number of times a query is aborted by the resource governor when a resource limit is exceeded
qtext Query text
Performance and Tuning Series: Query Processing and Abstract Plans 281
Using metrics
Using metrics
Use the information produced by QP metrics to identify:
• Query performance regression
• Most expensive query in a batch of running queries
• Most frequently run queries
When you have information on the queries that may be causing problems, you
can tune the queries to increase efficiency.
For example, identifying and fine-tuning an expensive query may be more
effective than tuning the cheaper ones in the same batch.
You can also identify the queries that are run most frequently, and fine-tune
them to increase efficiency.
Turning on query metrics may involve extra I/O for every query executed, so
there may be performance impact. However, also consider the benefits
mentioned above. You may want to gather statistical information from
monitoring tables instead of turning on metrics.
Both QP metrics and monitoring tables can be used to gather statistical
information. However, you can use QP metrics instead of the monitoring tables
to gather aggregated historical query information in a persistent catalog, rather
than have transient information from the monitor tables.
Examples
You can use QP metrics to identify specific queries for tuning and possible
regression on performance.
(4 rows affected)
The best candidate for tuning can be seen in the last statement of the above
results, which has the biggest value (164) for average logical IO.
Performance and Tuning Series: Query Processing and Abstract Plans 283
Clearing metrics
78 4
(4 rows affected)
The best candidate for tuning can be seen in the last statement of the above
results, which has the biggest value (78).
Clearing metrics
Use sp_metrics ‘flush’ to flush all aggregated metrics in memory to the system
catalog.The aggregated metrics for all statements in memory are set to zero.
sp_metrics ‘drop’, ‘@gid’ [, ‘@id'
These parameters let you filter out trivial metrics before writing metrics
information to the catalog.
By default, these configuration parameters are set to 0 (off).
For example, to not capture those query plans for which lio is less than 10, use:
sp_configure ‘metrics lio max’, 10
If you do not set any of these configuration parameters, Adaptive Server
captures the query metrics to the system tables. However, if you set any of
these configuration parameters, Adaptive Server uses only those nonzero
configuration parameters as thresholds for determining whether to capture
query metrics.
For example, if you set metrics elap max to a non-zero value, but no others,
query metrics are captured only if the elapsed time is bigger than the
configured value. Because the other three configuration parameters are set to
0, they do not act as thresholds for capturing metrics.
Performance and Tuning Series: Query Processing and Abstract Plans 285
Understanding the UID in sysquerymetrics
Note QP metrics for insert...selec, /update, delete statements are captured when
at least one table is involved. CIS-related queries and insert...values statements
are not included.
Performance and Tuning Series: Query Processing and Abstract Plans 287
Importance of statistics
• Statistics per index: index row count – index height; index leaf page count.
A local index has a separate systabstats row for each index partition. A
global index, which is considered a partitioned index with one partition,
has one systabstats row. Statistics per index: index row count can be found
in systabstats.
• Statistics per column: data distribution. Statistics per column be found in
sysstatistics.
Importance of statistics
The Adaptive Server cost-based optimizer uses statistics about the tables,
indexes, partitions, and columns named in a query to estimate query costs. It
chooses the access method that the optimizer determines has the least cost. But
this cost estimate cannot be accurate if statistics are not accurate.
Some statistics, such as the number of pages or rows in a table, are updated
during query processing. Other statistics, such as the histograms on columns,
are updated only when you execute update statistics, or when indexes are
created.
If your query is performing slowly and you seek help from Technical Support
or a Sybase newsgroup on the Internet, one of the first questions you are likely
be asked is “Did you run update statistics?” Use the optdiag command to see
when update statistics was last run for each column on which statistics exist:
Last update of column statistics: Aug 31 2004
4:14:17:180PM
Another command you may need for statistics maintenance is delete statistics.
Dropping an index does not drop the statistics for that index. If the distribution
of keys in the columns changes after the index is dropped, but the statistics are
still used for some queries, the outdated statistics can affect query plans.
Histogram statistics from a global index are more accurate than histogram
statistics generated by a local index. For a local index, statistics are created on
each partition, and are then merged to create a global histogram using
approximations as to how overlapping histogram cells from each partition
should be combined. With a global index, the merge step, with merging
estimates, does not occur. In most cases, there is no issue with update statistics
on a local index. However, if there are significant estimation errors in queries
involving partitioned tables, histogram accuracy can be improved by creating
and dropping a global index on a column rather than updating the statistics on
a local index.
Performance and Tuning Series: Query Processing and Abstract Plans 289
Updating statistics
Updating statistics
The update statistics command updates column-related statistics such as
histograms and densities. Statistics must be updated on those columns where
the distribution of keys in the index changes in ways that affect the use of
indexes for your queries.
Running update statistics requires system resources. Like other maintenance
tasks, Sybase recommends that you schedule it during at times when the load
on the server is light. In particular, update statistics requires table scans or leaf-
level scans of indexes, may increase I/O contention, may use the CPU to
perform sorts, and uses the data and procedure caches. Use of these resources
can adversely affect queries running on the server.
Using the sampling feature can reduce resource requirements and allow more
flexibility when running this task.
Note Sampling does not affect the update statistics table_name index_name
parameters]. Sampling affects only update index statistics and update all
statistics on unindexed columns and update statistics table_name (column_list).
In addition, some update statistics commands require shared locks, which may
block updates. See “Scan types, sort requirements, and locking” on page 307.
You can also configure Adaptive Server to automatically run update statistics
at times that have minimal impact on the system resources. See “Automatically
updating statistics” on page 294.
Performance and Tuning Series: Query Processing and Abstract Plans 291
Updating statistics
• using step values – identifies the number of steps used. The default is
20 steps. To change the default number of steps, use sp_configure.
sp_configure syntax includes the histogram tuning factor, which allows
a greater selection of the number of histogram steps. The default value
for histogram tuning factor is 20. See Chapter 5,4“Setting
Configuration Parameters” in System Administration Guide: Volume
1 for information about sp_configure.
When you are deciding whether or not to use sampling, consider the size of the
data set, the time constraints you are working with, and if the histogram
produced is as accurate as needed.
Performance and Tuning Series: Query Processing and Abstract Plans 293
Automatically updating statistics
The percentage to use when sampling depends on your needs. Test various
percentages until you receive a result that reflects the most accurate
information on a particular data set; for example:
update statistics authors(auth_id) with sampling = 5 percent
Set server-wide sampling percent using:
sp_configure 'sampling percent', 5
This command sets a server-wide sampling of 5% for update statistics that
allows you to execute update statistics without the sampling syntax. The
percentage can be between zero (0) and one hundred (100) percent.
Data change is a key metric that helps you measure the amount of altered data
since you last ran update statistics, and is tracked by the datachange function.
Using this metric and the criteria for resource availability, you can automate the
process of running update statistics. Job Scheduler includes a mechanism to
automatically run update statistics. and also includes customizable templates
you can use to determine when to run update statistics. These inputs include all
parameters to update statistics, the datachange threshold values, and the time to
run update statistics. Job Scheduler runs update statistics at a low priority so it
does not affect critical jobs that are running concurrently.
datachange function
The datachange function measures the amount of change in the data
distribution since update statistics last ran. Specifically, it measures the number
of inserts, updates, and deletes that have occurred on the given object, partition,
or column, and helps you determine if running update statistics would benefit
the query plan.
The syntax for datachange is:
select datachange(object_name, partition_name, colname)
Where:
• object_name – is the object name. This object is assumed to be in the
current database. The object_name cannot be null.
• partition_name – is the data partition name. This can be a null value.
• colname – is the column name for which the datachange is requested. This
can be a null value.
These parameters are all required.
datachange is expressed as a percentage of the total number of rows in the table
or partition (if the partition is specified). The percentage value can be greater
than 100 percent because the number of changes to an object can be much
greater than the number of rows in the table, particularly when the number of
deletes and updates to a table is very high.
The following set of examples illustrate the various uses for the datachange
function. The examples use the following:
• Object name is “O.”
• Partition name is “P.”
Performance and Tuning Series: Query Processing and Abstract Plans 295
Automatically updating statistics
Null partition and If you include null partition and column names, the value of datachange is
column names determined by the maximum value of the datachange for all columns that have
histograms summed across all partitions divided by the number of rows in the
table. The result is expressed as a percentage:
datachange = 100 * ( Max(data change value for (O, NULL, Ci))/rowcount(O)
Where i is 1 through the total number of columns with histograms (for
example, formatid 102 in sysstatistics).
This illustrates datachange gathering statistics:
create table matrix(col1 int, col2 int)
go
insert into matrix values (234, 560)
go
update statistics matrix(col1)
go
insert into matrix values(34,56)
go
select datachange ("matrix", NULL, NULL)
go
------
50.000000
The number of rows in matrix is two. The amount of data that has changed since
the last update statistics command is 1, so the datachange percentage is 100 *
1/2 = 50 percent.
datachange counters are all maintained in memory. These counters are
periodically flushed to disk by the housekeeper or when you run sp_flushstats.
Performance and Tuning Series: Query Processing and Abstract Plans 297
Configuring automatic update statistics
1 Install and set up Job Scheduler (Chapter 2, “Configuring and Running Job
Scheduler”)
2 Install the stored procedures required for the templates (Chapter 4, “Using
Templates to Schedule Jobs”).
3 Install the templates. Job Scheduler provides the templates specifically for
automating update statistics (Chapter 4, “Using Templates to Schedule
Jobs”).
4 Configure the templates. The templates for automating update statistics
are in the Statistics Management folder.
5 Schedule the job. After you have defined the index, column, or partition
you want tracked, you can also create a schedule that determines when
Adaptive Server runs the job, making sure that update statistics is run only
when it does not impact performance.
6 Identify success or failure. The Job Scheduler infrastructure allows you to
identify success or failure for the automated update statistic.
The template allows you to supply values for the various options of the update
statistics command such as sampling percent, number of consumers, steps, and
so on. Optionally, you can also provide threshold values for the datachange
function, page count, and row count. If you include these optional values, they
are used to determine when and if Adaptive Server should run update statistics.
If the current values for any of the tables, columns, indexes, or partitions
exceed the threshold values, Adaptive Server issues update statistics. Adaptive
Server detects that update statistics has been run on a column. Any query
referencing that table in the procedure cache is recompiled before the next
execution.
When does Adaptive There are many forms of the update statistics command (update statistics,
Server run update update index statistics, and so on); use these different forms depending on your
statistics?
needs.
You must specify three thresholds: rowcount, pagecount, and datachange.
Although values of NULL or 0 are ignored, these values do not prevent the
command from running.
Table 10-1 describes the circumstances under which Adaptive Server
automatically runs update statistics, based on the parameter values you provide.
Table 10-1: When does Adaptive Server automatically run update
statistics?
If the user Action taken by Job Scheduler
Specifies a datachange threshold of zero or NULL Runs update statistics at the scheduled time.
Performance and Tuning Series: Query Processing and Abstract Plans 299
Column statistics and statistics maintenance
In this example, the authors table is partitioned, and the user wants to run
update statistics when the data changes to the city column in the author_ptn2
partition are greater than or equal to 50%:
select @datachange = datachange("authors","author_ptn2", "city")
if @datachange >= 50
begin
update statistics authors partition author_ptn2(city)
end
go
The user can also specify that the script is executed when the system is idle or
any other parameters.
In this example, the user triggers update statistics when the data changes to the
city column of the authors table are greater than or equal to 100% (the table in
this example is not partitioned):
select @datachange = datachange("authors",NULL, "city")
if @datachange > 100
begin
update statistics authors (city)
end
go
• Dropping an index does not drop the statistics for the index, since the
optimizer can use column-level statistics to estimate costs, even when no
index exists.
To remove the statistics after dropping an index, you must explicitly delete
them using delete statistics.
If the statistics are useful to the query processor, and to keep the statistics
without having an index, use update statistics, specifying the column
name, for indexes where the distribution of key values changes over time.
• Truncating a table does not delete the column-level statistics in
sysstatistics. In many cases, tables are truncated and the same data is
reloaded.
Since truncate table does not delete the column-level statistics, you need
not run update statistics after the table is reloaded, if the data is the same.
If you reload the table with data that has a different distribution of key
values, run update statistics.
• You can drop and re-create indexes without affecting the index statistics,
by specifying 0 for the number of steps in the with statistics clause to create
index. This create index command does not affect the statistics in
sysstatistics:
create index title_id_ix on titles(title_id)
with statistics using 0 values
This allows you to re-create an index without overwriting statistics that
have been edited with optdiag.
• If two users attempt to create an index on the same table, with the same
columns, at the same time, one of the commands may fail due to an attempt
to enter a duplicate key value in sysstatistics.
• Executing update statistics on a column in a partition of a multipartition
table updates the statistics for that partition, but also updates the global
histogram for that column. This is done by merging the histograms for that
column from each partition in a row-weighted fashion to arrive at a global
histogram for the column.
• Updating statistics on a multipartitioned table for a column, without
specifying a partition, updates the statistics for each partition of the table
for that column, and, as a last step, merges the partition histograms for the
column to create a global histogram for the column.
• The optimizer only uses the global histograms for a multipartitioned table
during compilation, and does not read the partition histograms. This
approach avoids the overhead of merging partition histograms at
compilation time, and instead performs any merging work at DDL time.
Performance and Tuning Series: Query Processing and Abstract Plans 301
Creating and updating column statistics
• Columns included in a composite index, and which are not the leading
columns in the index, but which can help estimate the number of data rows
that need to be returned by a query
Performance and Tuning Series: Query Processing and Abstract Plans 303
Creating and updating column statistics
If this procedure is successful, you will not see the “NO STATS” messages
shown in Example 1 when you run the query again.
Note Running update statistics with a table name updates histograms and
densities for leading columns for indexes only; it does not update the statistics
for unindexed columns. To maintain these statistics, run update statistics and
specify the column name, or run update all statistics.
Performance and Tuning Series: Query Processing and Abstract Plans 305
Choosing step numbers for histograms
Note If your database was updated from a pre-11.9 version of Adaptive Server,
the number of steps defaults to the number of steps that were used on the
distribution page.
Increasing the number of steps beyond what is needed for good query
optimization can degrade Adaptive Server performance, largely due to the
amount of space that is required to store and use the statistics. Increasing the
number of steps:
• Increases the disk storage space required for sysstatistics
Performance and Tuning Series: Query Processing and Abstract Plans 307
Scan types, sort requirements, and locking
Performance and Tuning Series: Query Processing and Abstract Plans 309
Using the delete statistics command
• The cache space required for merging sort runs is taken from the data
cache, and some procedure cache space is also required. Setting the
number of sort buffers to a low value reduces the space used in the buffer
cache.
If number of sort buffers is set to a large value, it takes more space from the
data cache, and may also cause stored procedures to be flushed from the
procedure cache, since procedure cache space is used while merging
sorted values. There are approximately 100 bytes of procedure cache
needed for every row that can fit into the sort buffers specified. For
example, if 500 2K sort buffers are specified, and about 200 rows fit into
each 2K buffer, then 200 * 100 * 500 bytes of procedure cache are needed
to support the sort. This example requires about 5000 2K procedure cache
buffers, if the entire 500 data cache buffers are filled by a sort run.
Creating the worktables for sorts also uses space in tempdb.
Note delete statistics removes rows only from sysstatistics; it does not remove
rows from systabstats. You cannot delete the rows in systabstats that described
partition row counts, cluster ratios, page counts, and so on. However, if you use
optdiag simulate statistics to add any simulated systabstats rows to sysstatistics,
then those rows are deleted.
Note You must set the configuration parameter housekeeper free write percent
to 1 or greater to enable housekeeper statistics flushing.
Running dbcc checktable or dbcc checkdb updates these row count values in
memory.
Performance and Tuning Series: Query Processing and Abstract Plans 311
When row counts may be inaccurate
Topic Page
Overview 313
Managing abstract plans 314
Relationship between query text and query plans 315
Full versus partial plans 316
Abstract plan groups 318
How abstract plans are associated with queries 318
Overview
Adaptive Server can generate an abstract plan for a query, and save the
text and its associated abstract plan in the sysqueryplans system table.
Using a rapid hashing method, incoming SQL queries can be compared to
saved query text, and if a match is found, the corresponding saved abstract
plan is used to execute the query.
An abstract plan describes the execution plan for a query using a language
created for that purpose. This language contains operators to specify the
choices and actions that can be generated by the optimizer. For example,
to specify an index scan on the titles table, using the index title_id_ix, the
abstract plan says:
(i_scan title_id_ix titles)
To use this abstract plan with a query, you can modify the query text and
add a PLAN clause:
select * from titles where title_id = “On Liberty”
plan
“(i_scan title_id_ix titles)”
This alternative requires a change to the SQL text; however, the method
described in the first paragraph, that is, the sysqueryplans-based way to
give the abstract plan of a query, does not involve changing the query text.
Performance and Tuning Series: Query Processing and Abstract Plans 313
Managing abstract plans
Performance and Tuning Series: Query Processing and Abstract Plans 315
Full versus partial plans
(nl_join ( nl_join
( t_scan t2 )
( i_scan t1_c11_ix t1 )
)
( i_scan t3_c31_ix t3 )
)
( prop t3
( parallel 1 )
( prefetch 16 )
( lru )
)
( prop t1
( parallel 1 )
( prefetch 16 )
( lru )
)
( prop t2
( parallel 1 )
( prefetch 16 )
( lru )
)
If the abstract plan dump mode is on, the query text and the abstract plan
pair are saved in sysqueryplans:
select t1.c11, t2.c21
from t1, t2, t3
where t1.c11 = t2.c21
and t1.c11 = t3.c31
plan
“(i_scan t3_c31_ix t3)”
Performance and Tuning Series: Query Processing and Abstract Plans 317
Abstract plan groups
When abstract plan association is enabled, the hash key for incoming SQL
statements is computed, and this value is used to search for the matching
query and abstract plan in the current association group, with the
corresponding user ID. The full association key of an abstract plans
consists of:
• The user ID of the current user
• The group ID of the current association group
• The full query text
Once a matching hash key is found, the full text of the saved query is
compared to the query to be executed, and used if it matches.
The association key combination of user ID, group ID, and query text
means that for a given user, there cannot be two queries in the same
abstract plan group that have the same query text, but different query
plans.
SSQL_DESC 0x0x1474cd070
ssql_name *ss0626156152_0290084701ss*
ssql_hashkey 0x0x114a575d ssql_id 626156152
ssql_suid 1 ssql_uid 1 ssql_dbid 1 ssql_spid 0
ssql_status 0x0xa0 ssql_parallel_deg 1
Performance and Tuning Series: Query Processing and Abstract Plans 319
How abstract plans are associated with queries
ssql_isolate 1 ssql_tranmode 32
ssql_keep 0 ssql_usecnt 1 ssql_pgcount 6
ssql_optgoal allrows_mix ssql_optlevel ase_default
SQL TEXT: select * from t1 plan '(use optgoal allrows_mix)'
Use the set command to capture abstract plans and to associate incoming
SQL queries with saved plans. Any user can issue session-level
commands to capture and load plans during a session, and a system
administrator can enable server-wide abstract plan capture and
association. This chapter also describes how to specify abstract plans
using SQL.
Topic Page
Using set commands to capture and associate plans 321
set plan exists check option 327
Using other set options with abstract plans 328
Server-wide abstract plan capture and association modes 330
Creating plans using SQL 331
Performance and Tuning Series: Query Processing and Abstract Plans 321
Using set commands to capture and associate plans
Any set plan commands used in a stored procedure do not affect the
procedure (except those statements affected by deferred compilation) in
which they are included, but remain in effect after the procedure
completes.
The use of the use database command while set plan dump is in effect
disables plan dump mode.
Performance and Tuning Series: Query Processing and Abstract Plans 323
Using set commands to capture and associate plans
• If a valid plan exists for the query, it is loaded and used to optimize
the query.
• If a plan exists that is not valid (for example, because an index has
been dropped), a new plan is generated and used to optimize the
query, but is not saved.
• If only a partial plan exists, a full plan is generated, but the existing
partial plan is not replaced
• If a plan does not exist for the query, a plan is generated and saved.
With replace mode also enabled:
• If a valid plan exists for the query, it is loaded and used to optimize
the query.
• If the plan is not valid, a new plan is generated and used to optimize
the query, and the old plan is replaced.
• If the plan is a partial plan, a complete plan is generated and used, and
the existing partial plan is replaced. The specifications in the partial
plan are used as input to the optimizer.
• If a plan does not exist for the query, a plan is generated and saved.
Performance and Tuning Series: Query Processing and Abstract Plans 325
Using set commands to capture and associate plans
• If a valid plan exists for the query in the load group, it is loaded and
used.
• If the plan in the load group is not valid, a new plan is generated and
used to optimize the query. The new plan is saved in the dump group.
• If the plan in the load group is a partial plan, a full plan is generated
and saved in the dump group. The specifications in the partial plan are
used as input to the optimizer.
• If a plan does not exist for the query in the load group, a new plan is
generated. The new plan is saved in the dump group.
Note This changed behavior may effect the composition of the result set.
Sybase recommends that you review the result set created by the 15.0.2
versions of the set parameters before using them in your production
systems.
You must reset the set parameter before returning from the stored
procedure or the execution of subsequent stored procedures may be
affected. If you intend to propogate this change to subsequent stored
procedures, use export_options parameter.
• distinct_sorting
• distinct_hashing
• group_sorted
• group_hashing
• bushy_space_search
• parallel_query
• order_sorting
• nl_join
• merge_join
• hash_join
• append_union_all
• merge_union_all
• merge_union_distinct
• hash_union_distinct
• store_index
• index_intersection
• index_union
• multi_table_store_ind
• opportunistic_distict_view
• advanced_aggregation
• replicated_partition
• group_inserting
• basic_optimization
• auto_query_tuning
• query_tuning_mem_limit
• query_tuning_time_limit
Performance and Tuning Series: Query Processing and Abstract Plans 327
Using other set options with abstract plans
When set plan load and set exists check are both enabled, the hash keys for
up to 20 queries in the load group are cached for the user. If the load group
contains more than 20 queries, exists check mode is disabled. Each
incoming query is hashed; if its hash key is not stored in the abstract plan
cache, then there is no plan for the query and no search is made. This
speeds the compilation of all queries that do not have saved plans.
The syntax is:
set plan exists check {on | off}
You must enable load mode before you enable plan hash-key caching.
A system administrator can configure server-wide plan hash-key caching
with the configuration parameter abstract plan cache. To enable server-
wide plan caching, use:
sp_configure "abstract plan cache", 1
(2 rows affected)
Using showplan
When showplan is turned on, and abstract plan association mode has been
enabled with set plan load, showplan prints the plan ID of the matching
abstract plan at the beginning of the showplan output for the statement:
QUERY PLAN FOR STATEMENT 1 (at line 1).
Optimized using an Abstract Plan (ID : 832005995).
If you run queries using the plan clause added to a SQL statement,
showplan displays:
Using noexec
You can use noexec mode to capture abstract plans without actually
executing the queries. If noexec mode is in effect, queries are optimized
and abstract plans are saved, but no query results are returned.
To use noexec mode while capturing abstract plans, execute any needed
procedures (such as sp_add_qpgroup) and other set options (such as set
plan dump) before enabling noexec mode. The following example shows a
typical set of steps:
sp_add_qpgroup pubs_dev
go
Performance and Tuning Series: Query Processing and Abstract Plans 329
Server-wide abstract plan capture and association modes
Using fmtonly
A similar behavior can be obtained for capturing plans in stored
procedures without actually executing the stored procedures, using fmtonly
set.
sp_add_qpgroup pubs_dev
go
set plan dump pubs_dev on
go
set fmtonly on
go
exec stored_proc(...)
go
Using forceplan
If set forceplan on is in effect, and query association is also enabled for the
session, forceplan is ignored if a full abstract plan is used to optimize the
query. If a partial plan does not completely specify the join order:
• First, the tables in the abstract plan are ordered, as specified.
• The remaining tables are ordered as specified in the from clause.
• The two lists of tables are merged.
For information on writing plans, see Chapter 13, “Abstract Query Plan
Guide.”
Performance and Tuning Series: Query Processing and Abstract Plans 331
Creating plans using SQL
create plan
“select avg(price) from titles”
“(scalar_agg
(i_scan type_price_ix titles)
)”
The plan is saved in the current active plan group. You can also specify the
group name:
create plan
“select avg(price) from titles”
“(scalar_agg
(i_scan type_price_ix titles)
)”
into dev_plans
If a plan already exists for the specified query in the current plan group, or
the plan group that you specify, you must first enable replace mode in
order to overwrite the existing plan.
To see the plan ID that is used for a plan you create, create plan can return
the ID as a variable. You must declare the variable first. This example
returns the plan ID:
create plan
“select avg(price) from titles”
“(scalar_agg
(i_scan type_price_ix titles)
)”
into dev_plans
and set @id
select @id
When you use create plan, the query in the plan is not executed. This
means that:
• The text of the query is not parsed, so the query is not checked for
valid SQL syntax.
• The plans are not checked for valid abstract plan syntax.
• The plans are not checked to determine whether they are compatible
with the SQL text.
To guard against errors and problems, immediately execute the specified
query with showplan enabled.
• insert...select
• delete
• update
• if
• while
• return
Performance and Tuning Series: Query Processing and Abstract Plans 333
Creating plans using SQL
This chapter covers some guidelines you can use in writing Abstract
Plans.
Topic Page
Overview 335
Tips on writing abstract plans 363
Using abstract plans at the query level 363
Comparing plans before and after 366
Abstract plans for stored procedures 368
Ad hoc queries and abstract plans 370
Overview
Abstract plans allow you to specify the desired execution plan of a query.
Abstract plans provide an alternative to the session-level and query-level
options that force a join order, or specify the index, I/O size, or other query
execution options. The session-level and query-level options are
described in Chapter 12, “Creating and Using Abstract Plans.”
There are several optimization decisions that you cannot specify with set
commands or clauses in the query text, for example:
• Algorithms that implement a given relational operator; for example,
NLJ versus MJ versus HJ or GroupSorted versus GroupHashing versus
GroupInserting
• Subquery attachment
• The join order for flattened subqueries
• Reformatting
In many cases when issuing T-SQL commands, you cannot include set
commands or change the query text. Abstract plans provide an alternative,
more complete method of influencing optimizer decisions.
Performance and Tuning Series: Query Processing and Abstract Plans 335
Overview
Abstract plans are relational algebra expressions that are not included in
the query text. They are stored in a system catalog and associated with
incoming queries based on the text of these queries.
Performance and Tuning Series: Query Processing and Abstract Plans 337
Overview
Abstract plans can be full plans, specifying all optimizer choices for a
query, or can specify a subset of the choices, such as the index to use for a
single table in the query, but not the join order for the tables. For example,
using a partial abstract plan, you can specify that the query above should
use some index and let the optimizer choose between i_c11 and i_c12, but
not do a full table scan. The empty parentheses are used in place of the
index name:
(i_scan () t1)
In addition, the query could use either 2K or 16K I/O, or be performed in
serial or parallel.
Derived tables
A derived table is defined by the evaluation of a query expression and
differs from a regular table in that it is neither described in system catalogs
nor stored on disk. In Adaptive Server, a derived table may be a SQL
derived table or an abstract plan derived table.
• A SQL derived table – defined by one or more tables through the
evaluation of a query expression. A SQL derived table is used in the
query expression in which it is defined and exists only for the duration
of the query. See the Transact-SQL User’s Guide.
• An abstract plan derived table – a derived table used in query
processing, the optimization and execution of queries. An abstract
plan derived table differs from a SQL derived table in that it exists as
part of an abstract plan and is invisible to the end user.
Identifying tables
Abstract plans must name all of a query’s tables in a nonambiguous way,
such that a table named in the abstract can be linked to its occurrence in
the SQL query. In most cases, the table name is all that is needed. If the
query qualifies the table name with the database and owner name, these
are also needed to fully identify a table in the abstract plan. For example,
this example uses the unqualified table name:
select * from t1
The abstract plan also uses the unqualified name, (t_scan t1). If a database
name or owner name are provided in the query:
select * from pubs2.dbo.t1
Performance and Tuning Series: Query Processing and Abstract Plans 339
Overview
(join
(t_scan t1)
(i_scan i_c12 (table t1 (in (view v1))))
)
In abstract plans generated by Adaptive Server, the view or subquery-
qualified table names are generated only for the tables where they are
needed to remove name ambiguity. For other tables, only the name is
generated.
In abstract plans created by the user, view or subquery-qualified tables
names are required in case of ambiguity; both syntaxes are accepted
otherwise.
Identifying indexes
The i_scan operator requires two operands, the index name and the table
name, as shown here:
(i_scan i_c12 t1)
To specify that some index should be used, without specifying the index,
substitute empty parenthesis for the index name:
(i_scan () t1)
Performance and Tuning Series: Query Processing and Abstract Plans 341
Overview
(join
(join
(scan t2)
(scan t1)
)
(scan t3)
)
The results of the t2-t1 join are then joined to t3. The scan operator in this
example leaves the choice of table scan or index scan up to the optimizer.
(join
(join
...
(join
(join
(scan t1)
(scan t2)
)
(scan t3)
)
...
(scan tN-1)
)
(scan tN)
)
This notation can be used as shorthand for the nl_join operator:
(nl_join
(scan t1)
(scan t2)
(scan t3)
...
(scan tN-1)
(scan tN)
)
Performance and Tuning Series: Query Processing and Abstract Plans 343
Overview
The join operators are generic in that they implement any of the outer
joins, inner joins, and existence joins; the optimizer chooses the correct
join semantics according to the query semantics.
)
Since the table names are not ambiguous, the view qualification is not
needed. However, the following abstract plan is also legal and has the
same meaning:
(nl_join
(scan (table t2(in(view v2))))
(scan t1)
(scan (table t3 (in (view v2))))
)
This example joins with t3 in the view:
select * from t1, v2
where c11 = c31
and c32 = 100
This plan uses the join order t3, t1, t2:
(join
(scan t3)
(scan t1)
(scan t2)
)
This is an example where abstract plans can be used, if needed, to affect
the join order for a query, when set forceplan cannot.
Performance and Tuning Series: Query Processing and Abstract Plans 345
Overview
)
The nested-loop plan uses the index i_c11to limit the scan using the search
clause, and then performs the join with t2, using the index on the join
column.
This merge-join plan uses different indexes:
(m_join
(i_scan i_c12 t1)
(i_scan i_c21 t2)
)
The merge join uses the indexes on the join columns, i_c12 and i_c21, for
the merge keys. This query performs a full-merge join and no sort is
needed.
A merge join could also use the index on i_c11 to select only the matching
rows, but then a sort is needed to provide the needed ordering.
(m_join
(sort
(i_scan i_c11 t1)
)
(i_scan i_c21 t2)
)
Finally, this plan does a hash join and a full table scan on the inner side:
(h_join
(i_scan i_c11 t1)
(t_scan t2)
)
Performance and Tuning Series: Query Processing and Abstract Plans 347
Overview
(scan t1)
)
(join
(scan t1)
(scan t2)
)
)
When the query associated with the plan is executed, the query cannot be
compiled, and an error is raised.
Other inconsistent hints do not raise an exception, but may use any of the
specified access methods. This plan specifies both an index scan and a
table scan for the same table:
(hints
(t_scan t3)
(i_scan () t3)
)
In this case, either method may be chosen, and the behavior is
indeterminate.
Performance and Tuning Series: Query Processing and Abstract Plans 349
Overview
Materialized subqueries
This query includes a noncorrelated subquery that can be materialized:
select *
from t1
where c11 = (select count(*) from t2)
The first step in the abstract plan materializes the scalar aggregate in the
subquery. The second step uses the result to scan t1:
( sequence
(scalar_agg
(i_scan i_c21 t2)
)
(i_scan i_c11 t1)
)
Flattened subqueries
Some subqueries can be flattened into joins. The join, nl_join, m_join, and
h_join operators leave it to the optimizer to detect when an existence join
is needed. For example, this query includes a subquery introduced with
exists:
select * from t1
where c12 > 0
and exists (select * from t2
where t1.c11 = c21 and c22 < 100)
The semantics of the query require an existence join between t1 and t2. The
join order t1, t2 is interpreted by the optimizer as a semijoin, with the scan
of t2 stopping on the first matching row of t2 for each qualifying row in t1:
(join
(scan t1)
(scan t2)
)
The join order t2, t1 requires other means to guarantee the duplicate
elimination:
(join
(distinct
(scan t2)
)
(scan t1)
)
(nl_join
(scan t1)
(scan t2)
(scan t3)
)
If the join increases the number of times that t3 needs to be scanned, this
abstract plan performs the scans of t3 before the join:
(nl_join
(scan t1)
(scan t3)
(scan t2)
)
Performance and Tuning Series: Query Processing and Abstract Plans 351
Overview
Nested subqueries
Nested subqueries can be explicitly described in abstract plans if:
• The abstract plan for the subquery is provided.
• The location at which the subquery attaches to the main query is
specified.
Abstract plans allow you to affect the query plan for the subquery, and to
change the attachment point for the subquery in the outer query.
The nested operator specifies the position of the subquery in the outer
query. Subqueries are “nested over” a specific abstract plan derived table.
The optimizer chooses a spot where all the correlation columns for the
outer query are available, and where it estimates that the subquery needs
to be executed the least number of times.
The following SQL statement contains a correlated expression subquery:
select *
from t1, t2
where c11 = c21
and c21 > 100
and c12 = (select c31 from t3
where c32 = t1.c11)
The abstract plan shows the subquery nested over the scan of t1:
(nl_join
(nested
(i_scan i_c12 t1)
(subq
(scalar_agg
(scan t3)
)
)
)
(i_scan i_c21 t2)
)
Aggregation is described in Chapter 2, “Using showplan.” The scalar_agg
abstract plan operator is necessary because all abstract plans, even partial
ones, must be complete down to the leafs.
Performance and Tuning Series: Query Processing and Abstract Plans 353
Overview
)
(subq
(scalar_agg
(i_scan i_c13 (table t1 (in (subq 2))))
)
)
)
In this query, the second subquery is nested in the first:
select * from t1
where c11 not in
(select c12 from t1
where c11 not in
(select c13 from t1)
In this case, the subquery that projects out of c12 is also named “1” and the
subquery that projects out of c13 is also named “2”.
(nested
(t_scan t1
(subq
(scalar_agg
(nested
(i_scan i_c12 (table t1 (in (subq 1))))
(subq
(scalar_agg
(i_scan i_c21 (table t1 (in (subq 2))))
)
)
)
)
)
)
In the plan, the join order is t1, t2, t3, with the subquery nested over the
scan of t1:
(nl_join
(nested
(i_scan i_c11 t1)
(subq
(t_scan (table t2 (in (subq 1)))
)
)
(i_scan i_c21 t2)
(i_scan i_c32 t3)
)
(nl_join
(nested
(nl_join
(i_scan i_c11_c12 t1)
(i_scan i_c22 t2)
)
(subq
(t_scan (table t3 (in (subq 1))))
)
)
(i_scan i_c32 t3)
)
Since the subquery requires columns from both outer tables, it would be
incorrect to nest it over the scan of t1 or the scan of t2; such errors are
silently corrected during optimization.
Performance and Tuning Series: Query Processing and Abstract Plans 355
Overview
However, the following abstract plan makes the legal request to nest the
subquery over the three-table join:
(nested
(nl_join
(i_scan i_c11_c12 t1)
(i_scan i_c22 t2)
(i_scan i_c32 t3)
)
(subq
(t_scan (table t3 (in (subq 1))))
)
)
select *
from v3 a, v3 b
where a.c31 = b.c31
In such a case, the abstract plan exposes the worktable and the store
operator that materializes it. The two scans of the worktable are identified
through their correlation names:
(sequence
(store
(group_sorted
(i_scan i_c31 t3)
)
)
(m_join
(sort
(t_scan (work_t (a Worktable)))
( sort
(t_scan (work_t (b Worktable)))
)
)
)
The handling of vector aggregation in an abstract plan is described in the
next section.
(scalar_agg
(i_scan ic11 t1)
)
Since the scalar aggregate is the top abstract plan operator, removing it and
using the following partial plan has the same outcome:
(i_scan ic11 t1)
The scalar_agg abstract plan is typically needed when it is part of a
subquery and the abstract plan must cover the parent query as well.
Vector aggregation is different, in that there are several physical operators
to implement the group logical operator, which means that the optimizer
has a choice to make. Thus, the abstract plan can force it.
select max(c11)
from t1
group by c12
The following abstract plan examples force each of the three vector
aggregation algorithms:
(group_sorted
(i_scan i_c12 t1)
)
(group_hashing
(t_scan t1)
Performance and Tuning Series: Query Processing and Abstract Plans 357
Overview
(group_inserting
(t_scan t1)
)
Performance and Tuning Series: Query Processing and Abstract Plans 359
Overview
The store_index abstract plan operator must be placed on the inner side of
an nl_join. It can be placed over any abstract plan; there is no longer a
single table scan limitation. The legacy (scan (store... )) syntax is
still accepted.
Performance and Tuning Series: Query Processing and Abstract Plans 361
Overview
The abstract plan xchg operator forces the optimizer to repartition, on-the-
fly, in n ways, its child-derived table. The abstract plan only gives the
degree. The optimizer chooses the most useful partitioning columns and
style (hash, range, list, or round-robin).
In the following example, assume that t1 and t2 are hash partitioned two
ways and three ways on the join columns, and i_c21 is a local index:
select *
from t1, t2
where c11=c21
The following abstract plan repartitions t1 three ways, does a three-way
parallel nl_join, serializes the results, and returns a single data stream to the
client:
(xchg 1
(nl_join
(xchg 3
(t_scan t1)
)
(i_scan i_c21 t2)
)
)
It is not necessary to specify t2’s parallel scan. It is hash-partitioned three
ways, and, as it is joined with an xchg-3, no other plan is legal.
The following abstract plan scans and sorts t1 and t2 in parallel, as each is
partitioned, then serializes them for the m_join:
(m_join
(xchg 1
(sort
(t_scan t1)
)
)
(xchg 1
(sort
(t_scan t2)
)
)
)
(prop t1 (parallel 2))
(prop t2 (parallel 3))
The parallel abstract plan construct is used to make sure that the optimizer
chooses the parallel scan with the native degree.
Performance and Tuning Series: Query Processing and Abstract Plans 363
Using abstract plans at the query level
set
nl_join|merge_join|hash_join|...
on | off
The use ... abstract plan syntax accepts any number of use forms before
the abstract plan derived table. In versions of Adaptive Server earlir than
15.0, optgoal and opttimeout could not be in the same abstract plan with a
derived table. For example, this statement would need to be separate from
a optgoal statement in a query:
select ...
plan
"(use opttimeoutlimit 10) (i_scan r)"
However, you can include several statements in the same abstract plan by:
• Using several use statements. For example:
select ...
plan
"(use optgoal allrows_dss) (use nl_join off)
(...)"
• Placing several items within one use form. For example:
select ...
plan
"(use (optgoal allrows_dss) (nl_join off))
(...)"
At the query level, use the optimization goal (opt_goal) or timeout
(opttimeout) setting with the use ... abstract plan syntax. At the session
level, use these settings with the set plan ... syntax:
• Optimization goal.
• Optimization timeout
For example, join r outer to s and enable the hash_join without an
optimization goal (opt_goal):
select ...
>
> plan
>
"(use hash_join on)
>
> (join (scan r) (scan s))"
This example uses the opt_goal and allrows_oltp statements, but with
hash_join enabled:
select ...
>
> plan
>
> "(use opt_goal allrows_oltp)(use hash_join
on)"
When setting the optimization goal and the optimization criteria at the
query level, the order of the use statements does not affect the outcome.
• The abstract plan optimization goal is set first, and sets the
optimization goal defaults for the optimization criteria.
• You can set abstract plan optimization, which supersedes
optimization goal defaults criteria, you set the optimization goal.
plan
plan
Performance and Tuning Series: Query Processing and Abstract Plans 365
Comparing plans before and after
3 Copy all plans from ap_stdout to ap_stdin (or some other group, if you
do not want to use server-wide plan load mode), using
sp_copy_all_qplans.
Performance and Tuning Series: Query Processing and Abstract Plans 367
Abstract plans for stored procedures
• The procedure must be executed with plan capture mode enabled, and
the procedure must be read from disk, not from the procedure cache.
This sequence of steps captures the query text and abstract plans for all
statements in the procedure that can be optimized:
set plan dump dev_plans on
go
create procedure myproc as ...
go
exec myproc
go
If the procedure is in cache, and the plans for the procedure are not being
captured, execute the procedure with recompile. Similarly, once a stored
procedure has been executed using an abstract query plan, the plan in the
procedure cache is used so that query plan association does not take place
unless the procedure is read from disk.
You can use set fmtonly on to capture plans for a stored procedure without
actually executing the statements in a stored procedure.
Performance and Tuning Series: Query Processing and Abstract Plans 369
Ad hoc queries and abstract plans
If you run the query a second time (without recompilation) with different
parameters that use a different code path, because Adaptive Server already
optimized and saved the plans for all statements from the earlier
compilation, both the plans and the abstract plans for the statements in this
different code path are available, and are based on the prior stored
procedure’s run parameter values, whether or not these statement were
executed.
However, abstract plans for procedures do not solve the problem caused
by procedures with statements that are optimized differently depending on
conditions or parameters. For example is a procedure where users provide
the low and high values for a between clause, with a query such as:
select title_id
from titles
where price between @lo and @hi
Depending on the parameters, the best plan could either be an index access
or a table scan. The abstract plan may specify either access method,
depending on the parameters used for the initial execution of the
procedure. Abstract plans that are saved while executing queries or stored
procedures in tempdb are lost if the server is restarted.
Performance and Tuning Series: Query Processing and Abstract Plans 371
Ad hoc queries and abstract plans
Creating a group
Use sp_add_qpgroup to create and names an abstract plan group. Unless
you are using the default capture group, ap_stdout, you must create a plan
group before you can begin capturing plans. For example, to start saving
plans in a group called dev_plans, you must create the group, then issue
the set plan dump command, specifying the group name:
sp_add_qpgroup dev_plans
set plan dump dev_plans on
/*SQL queries to capture*/
Only a system administrator or database owner can add abstract plan
groups. Once a group is created, any user can dump or load plans from the
group.
Performance and Tuning Series: Query Processing and Abstract Plans 373
Managing an abstract plan group
Dropping a group
Use sp_drop_qpgroup to drop an abstract plan group.
The following restrictions apply to sp_drop_qpgroup:
• Only a system administrator or database owner can drop abstract plan
groups.
• You cannot drop a group that contains plans. To remove all plans from
a group, use sp_drop_all_qplans, specifying the group name.
• You cannot drop the default abstract plan groups ap_stdin and
ap_stdout.
When you use sp_help_qpgroup with a group name, the report provides
statistics about plans in the specified group. This example reports on the
group ptest2:
sp_help_qpgroup ptest2
Query plans group 'ptest2', GID 7
----------- ----------------
452 189
sysqueryplans rows consumption, number of query
plans per row count
Rows Plans
----------- -----------
5 2
3 68
2 119
Query plans that use the most sysqueryplans rows
Rows Plan
----------- -----------
5 1932533918
5 1964534032
Hashkeys
-----------
123
There is no hash key collision in this group.
When reporting on an individual group, sp_help_qpgroup reports:
• The total number of abstract plans, and the total number of rows in the
sysqueryplans table.
Performance and Tuning Series: Query Processing and Abstract Plans 375
Managing an abstract plan group
Renaming a group
A system administrator or database owner can rename an abstract plan
group with sp_rename_qpgroup. This example changes the name of the
group from dev_plans to prod_plans:
Performance and Tuning Series: Query Processing and Abstract Plans 377
Managing individual abstract plans
Viewing a plan
Use sp_help_qplan to report on individual abstract plans. It provides three
types of reports that you can specify: brief, full, and list. The brief report
prints only the first 78 characters of the query and plan; use full to see the
entire query and plan, or list to display only the first 20 characters of the
query and plan.
This example prints the default brief report:
sp_help_qplan 588529130
gid hashkey id
----------- ----------- -----------
8 1460604254 588529130
query
---------------------------------------------------------------
select min(price) from titles
plan
---------------------------------------------------------------
(plan
(i_scan type_price titles)
()
)
(prop titles
(parallel ...
A system administrator or database owner can use sp_help_qplan to report
on any plan in the database. Other users can view only the plans that they
own.
sp_help_qpgroup reports on all plans in a group. See “Getting information
about a group” on page 374.
• The query and plan you are trying to copy already exists in the
destination group
• Another plan in the group has the same user ID and hash key
• Another plan in the group has the same hash key, but the queries are
different
If there is a hash-key collision, the plan is copied. If the plan already exists
in the destination group or if it would give an association key collision, the
plan is not copied. The messages printed by sp_copy_qplan contain the
plan ID of the plan in the destination group, so you can use sp_help_qplan
to check the query and plan.
A system administrator or the database owner can copy any abstract plan.
Other users can copy only plans that they own. The original plan and group
are not affected by sp_copy_qplan. The copied plan is assigned a new plan
ID, the ID of the destination group, and the user ID of the user who ran the
query that generated the plan.
Performance and Tuning Series: Query Processing and Abstract Plans 379
Managing individual abstract plans
A system administrator or database owner can change the abstract plan for
any saved query. Other users can modify only plans that they own.
When you execute sp_set_qplan, the abstract plan is not checked against
the query text to determine whether the new plan is valid for the query, or
whether the tables and indexes exist. To test the validity of the plan,
execute the associated query.
You can also use create plan and the plan clause to specify the abstract plan
for a query. See “Creating plans using SQL” on page 331.
Performance and Tuning Series: Query Processing and Abstract Plans 381
Managing all plans in a group
A new plan ID is assigned to each copied plan. The copied plans have the
original user’s ID. To copy abstract plans and assign new user IDs, you
must use sp_export_qpgroup and sp_import_qpgroup. See “Importing and
exporting groups of plans” on page 385.
A system administrator or database owner can copy all plans in the
database. Other users can copy only plans that they own.
count
-----------
25
Query plans present only in group ’ap_stdout’ :
count
-----------
0
Query plans present only in group ’ap_stdin’ :
count
-----------
1
count
-----------
39
Different query plans that have the same association key
count
-----------
4
ptest1 ptest2
id1 id2
----------- -----------
Performance and Tuning Series: Query Processing and Abstract Plans 383
Managing all plans in a group
764529757 1580532664
780529814 1596532721
796529871 1612532778
908530270 1724533177
Query plans present only in group ’ptest1’ :
count
-----------
3
id
-----------
524528902
1292531638
1308531695
count
-----------
1
id
-----------
2108534545
Performance and Tuning Series: Query Processing and Abstract Plans 385
Importing and exporting groups of plans
You cannot copy plans into a group that already contains plans for the
specified user.
Performance and Tuning Series: Query Processing and Abstract Plans 387
Index
E H
elimination partition 197 Halloween problem
emit operator 52 cursors and 268
enable parallelism 135 hash join
engine query execution 20 operator 74
equi-join, transitive closure 7 hash union
exceptions, optimization goals 16 operator 91
exchange hash vector aggregate
operator 148 operator 86
worker process mode 151 hash-based table scan 157
exchange, pipemanagement 149 HashDistinctOp operator 83
execute cursors histograms
memory use of 266 join 12
executing steps, number of 306
query processing metrics 280
execution
preventing with set noexec on 37
exists check mode 327 I
expensive direct updates 29 I/O
exporting plan groups 385 direct updates and 27
expressions, join 14 prefetch keyword 236
range queries and 236
specifying size in queries 236
update operations and 29
F IDENTITY columns
factors, analyzed for optimization 5 cursors and 268
fetching cursors, memory and 266 importing abstract plan groups 385
finding abstract plans 377 index scan 159
fixed-length columns clustered 163
indexes and update modes 36 clustered, partitioned table 163
for update option, declare cursor covered using nonclustered global 162
optimizing and 276 global nonclustered 159
forceplan option, set 232 nonclustered, partitioned table 163
from table 54 noncovered, global nonclustered 159
function indexes
datachange , statistics 295 cursors using 267
large I/O for 236
search arguments 10
specifying for queries 234
G update index statistics on 305
goals, optimization 15 update modes and 35
goals, optimization exceptions 16 update operations and 28, 29
group sorted agg update statistics on 305
operator 85 in-place updates 27
group sorted agg operator 85 insert
GroupSorted (Distinct) operator 81 operator 62, 195
Performance and Tuning Series: Query Processing and Abstract Plans 389
Index
N
K names
keys, index index clause and 235
update operations on 28 index prefetch and 237
nary nested loop join operator 76
nested loop join 70
networks
L cursor activity of 272
large I/O nonequality, operators 11
index leaf pages 236 nonleading columns sort statistics 308
Lava null columns
operators 23 optimizing updates on 35
Performance and Tuning Series: Query Processing and Abstract Plans 391
Index
Performance and Tuning Series: Query Processing and Abstract Plans 393
Index
Performance and Tuning Series: Query Processing and Abstract Plans 395
Index
V
variables, setting local 145
vector aggregation 180
distinct 184
in-partitioned 180
repartitioned 181
serial 183
two-phased 182
vector aggregation operators 84
view
sysquerymetrics, query processing metrics 282
W
with statistics clause, create index command 301
worker process mode
exchange 151
worker processes
setting number 135
X
XML
diagnostic output 112
discontinued trace commands 119
permissions 122
set 112