[dba-SQLServer] How to construct queries

Asger Blond ab-mi at post3.tele.dk
Fri Dec 11 18:44:11 CST 2009


John,
It will be useful for you to explore the execution plans for your queries.
You do so by highlighting the query in SSMS and pressing CTRL+L.
The Execution Plan window may be overwhelming but you just have to focus on
the rightmost ikon which is indicating either "Table scan" or "Index Scan"
or "Index seek".
Now for your question:
To understand what's going on you have to distinguish *fetch operation* and
*search operation*. You also have to distinguish a *composite index* and an
*index with included columns*.
A covering index means that SQL Server can *fetch* the records you are
selecting in your query from the leaf level (=bottom level) of your
index-tree, not having to make a lookup to the data pages.
But a covering index doesn't mean that SQL Server can use your index for its
*search* operations. SQL Server can only use the index for a *search*
operation if your where-condition references a field which is the *first*
field defined in the index.
And to make things more complicated: A covering index can be accomplished in
to ways: as a *composite index* or as an *index with included columns*.
A composite index is created this way:
CREATE INDEX MyCompositeIndex ON MyTable (FieldA, FieldB, FieldC, FieldD)
An index with included columns is created this way:
CREATE INDEX MyIncludedIndex ON MyTable (FieldA) INCLUDE (FieldB, FieldC,
FieldD)
A *composite index* records all specified columns in every level of the
index-tree, and it is very useful for multi-field *sorting* operations. But
it is not useful for multi-field *search" operations, because SQL Server
will only consider using this index for a query referencing the *first*
field (here FieldA) in the where-condition. It is however very useful for
*fetch* operations, since SQL Server then can do with an "index scan" on the
leaf level of the index not having to make a lookup the actual data-pages.
This is only true however if you are selecting fields which are present in
the index: if your query selects fields not defined in an index then SQL
Server has to use a *table scan* which is not efficient. And now you may
ask: What about a SELECT COUNT(*) - isn't this referencing all fields and
shouldn't this per definition prevent SQL Server from using the index? Don't
worry: COUNT(*) is a special case where SQL Server will use the index if it
can make the execution more efficient.
An *index with included columns* only records the included columns in the
leaf level of the index. For this reason it is neither useful for
multi-field *sorting* operations nor for *search* operations on the included
columns. But as for *composite indexes* it is very useful for *fetch*
operations.
Bottom line:
A *composite index* is good for multi-columns sorting and covering queries,
but it doesn't help *search* operations and it imposes an overhead forcing
SQL Server to record all fields in every level of the index-tree. An *index
with included column* it good for covering queries too, but it doesn't help
multi-columns sorting and it doesn't help *search* operations either -
however it imposes fare less overhead cause the included fields are only
recorded in the leaf-level of the index.

I suggest you play whith the different options using CTRL+L - pretty soon
you will notice this:

Composite index on FieldA, FieldB, FieldC
Composite index on FieldD, FieldE, FieldF
SELECT COUNT (*) FROM MyTable WHERE FieldA="Y" --> Index Seek
SELECT COUNT (*) FROM MyTable WHERE FieldB="Y" --> Index Scan
SELECT COUNT (*) FROM MyTable WHERE FieldD="Y" --> Index Seek
SELECT COUNT (*) FROM MyTable WHERE FieldE="Y" --> Index Scan
SELECT COUNT (*) FROM MyTable WHERE FieldA="Y" OR FieldB="Y" OR FieldC="Y"
--> Index Scan
SELECT COUNT (*) FROM MyTable WHERE FieldA="Y" OR FieldD="Y" --> Perhaps two
index seeks merged
SELECT COUNT (*) FROM MyTable WHERE FieldA="Y" OR FieldE="Y" --> Probably a
table scan

Index on FieldA including FieldB and FieldC
Index on FieldA including FieldD and FieldF
--> exactly same results

Another option is to use a separate index on each field. Then you will
notice:
SELECT COUNT (*) FROM MyTable WHERE FieldA="Y" --> Index Seek
SELECT COUNT (*) FROM MyTable WHERE FieldA="Y" OR FieldB="Y" --> Perhaps two
index seeks merged
SELECT COUNT (*) FROM MyTable WHERE FieldA="Y" OR FieldB="Y" OR FieldC="Y"
--> Probably a table scan

Mind that the results will depend on the actual values present in your
table. SQL Server maintains statistics of the indexed columns and if there
is a low selectivity for you query (= a high rate of matching values in the
indexed search-field) then chance is that SQL Server don't want to use the
index and will prefer using a table scan instead.

 Asger



-----Oprindelig meddelelse-----
Fra: dba-sqlserver-bounces at databaseadvisors.com
[mailto:dba-sqlserver-bounces at databaseadvisors.com] På vegne af jwcolby
Sendt: 11. december 2009 19:29
Til: Dba-Sqlserver
Emne: [dba-SQLServer] How to construct queries

I often have to do things like "supply counts of records where..." and the
list will be something like:

FieldA = "Y" or FieldB = "Y" or FieldC = "Y" ...

Often times the counts must be for sets of fields, for example FieldA
through FieldG or FieldX 
through FieldZ

These fields are usually a small minority of the total 50 million records,
sometimes a million, 
sometimes 5 million etc.

So I have routinely created cover indexes for the groups, one index for
FieldA..FieldG, another 
index for FieldX .. FieldZ.

If a query requests data from fields in both groups, will the query use both
cover indexes to grab 
the data for the records?

Is there a more efficient way to do these kinds of counts?

-- 
John W. Colby
www.ColbyConsulting.com
_______________________________________________
dba-SQLServer mailing list
dba-SQLServer at databaseadvisors.com
http://databaseadvisors.com/mailman/listinfo/dba-sqlserver
http://www.databaseadvisors.com





More information about the dba-SQLServer mailing list