Groups | Blog | Home
all groups > sql server (alternate) > march 2004 >

sql server (alternate) : QEP problem



Laurence Breeze
3/12/2004 3:57:21 PM
I have 2 identical databases (one test and one to be live soon) in
different installations. Both have an identical table with
approximately 4.5 million rows and a row width of 339 characters. Both
tables have the same primary key constraint and clustered and
non-clustered indexes. Both tables have the same data (one was loaded
from the other and statistics are up to date.

My problem is that when running a select like:

select *
from tablename
where keycol = 'value'

in the test database the response is very fast and the QEP shows that
SQL Server is doing a 'clustered index seek' and the I/O cost is 0.003
and the CPU cost is 0.00008. In the live database the QEP shows that
SQL Server is doing an 'index scan' and the I/O cost is 9.4 and the CPU
cost is 2.5.

Whilst I'm not sure what these cost units are I can see that the 2 QEPs
have very different costs and the QEP is very different. Interestingly,
running the following query in both databases:

select count(*)
from tablename

leads to QEPs that are identical to the previous QEP in the live
database.

I've run sp_dboptions for both databases and they are the same.

Whilst these databases are in different installations and on different
machines, the OS and SQL Server software are identical and shouldn't
lead to a different QEP. It looks to me that SQL Server is just
ignoring the statistics and ignoring the fact that there is a primary
key constraint on the column in the where clause.

Has anyone any ideas how to investigate this further or solve the
problem ?

Thanks in advance.

Laurence Breeze
The Open University
Erland Sommarskog
3/12/2004 10:17:29 PM
[posted and mailed, please reply in news]

Laurence Breeze (laurence_breeze@yahoo.co.uk) writes:
[quoted text, click to view]

If you had had an Index Seek in once case, and a Clustered Index Scan,
I would have discussed table scans contra bookmark lookups.

But if you have Clustered Index Seek in once case, and Index Scan
in the other, I get a feeling that the clustered index is missing
in the live database.

If this guess is incorrect, can you post the CREATE TABLE statement for the
table, and the output from sp_helpindex on both servers for the table? Of
course, I would like to see the actual query as well.

[quoted text, click to view]

That query should be carried out your most narrow (in terms of key
length) non-clustered index.




--
Erland Sommarskog, SQL Server MVP, sommar@algonet.se

Books Online for SQL Server SP3 at
Laurence Breeze
3/15/2004 10:50:13 AM
Hi,

I don't have the original create script. I've extracted a script for
the create table and create index statements from SQLServer using Query
Analyser for both databases, see below:

CREATE TABLE [LTS_leu_table] (
[PersId] [varchar] (14) COLLATE Latin1_General_CI_AS NOT NULL ,
[OUcu] [varchar] (14) COLLATE Latin1_General_CI_AS NULL ,
[Title] [varchar] (40) COLLATE Latin1_General_CI_AS NULL ,
[Name] [varchar] (64) COLLATE Latin1_General_CI_AS NULL ,
[Address] [varchar] (255) COLLATE Latin1_General_CI_AS NULL ,
[Country] [varchar] (12) COLLATE Latin1_General_CI_AS NULL ,
[Region] [varchar] (20) COLLATE Latin1_General_CI_AS NULL ,
[DayPhone] [varchar] (32) COLLATE Latin1_General_CI_AS NULL ,
[EvePhone] [varchar] (32) COLLATE Latin1_General_CI_AS NULL ,
[Email] [varchar] (60) COLLATE Latin1_General_CI_AS NULL ,
[Fax] [varchar] (32) COLLATE Latin1_General_CI_AS NULL ,
[DoB] [varchar] (20) COLLATE Latin1_General_CI_AS NULL ,
[MasterStatus] [varchar] (3) COLLATE Latin1_General_CI_AS NULL ,
[UserType] [int] NULL ,
PRIMARY KEY CLUSTERED
(
[PersId]
) WITH FILLFACTOR = 90 ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE UNIQUE INDEX [PersId_ind] ON [dbo].[LTS_leu_table]([PersId])
WITH FILLFACTOR = 90 ON [PRIMARY]
GO

CREATE INDEX [OUcu_ind] ON [dbo].[LTS_leu_table]([OUcu]) WITH
FILLFACTOR = 90 ON [PRIMARY]
GO

The only difference between the scripts for the 2 databases is that the
script for the test database has fillfactors set whilst the script for
the live database doesn't.

My original query was:

select * from lts_leu_table where persid = '12345678'

The QEP shows an index scan using the PersId_ind index with a bookmark
lookup.

The following query:

select count(*) from lts_leu_table

has a QEP which also shows an index scan using the PersID_ind index and
has the same costs as the query above.


Since my original post I've tried dropping the indexes and recreating
them with no change to the QEPs.

The results of running sp_helpindex on the table are:

index_name index_description
index_keys
---------------------------
--------------------------------------------------- -----------
OUcu_ind nonclustered located on PRIMARY
OUcu
PersId_ind nonclustered, unique located on PRIMARY
PersId
PK__LTS_leu_new__4C214075 clustered, unique, primary key located on
PRIMARY PersId

I have also tried running a similar query to my original one that I
would expect to use the other non-clustered index, i.e:

select * from lts_leu_table where oucu = 'ilb3'

The QEP for this query also shows an index scan, this time using the
OUcu_ind index with a bookmark lookup.

It seems very odd that the appropriate index is used in both cases but
not in the way I would have expected.

Laurence Breeze



[quoted text, click to view]
Erland Sommarskog
3/15/2004 10:45:21 PM
Laurence Breeze (laurence_breeze@yahoo.co.uk) writes:
[quoted text, click to view]

Aha! So there is both a clustered index and a non-clustered index on
the primary key.

That at least explains how come an Index Scan wins over a Clustered
Index Seek. The answer to that one is that for some reason, SQL Server
can not seek the index. In this case, scanning the non-clustered index
is certainly cheaper than scanning the clustered index.

But why it has to scan, I don't really have a grip on right now. The
typical reason for this, is that the column value is being auto-converted.
And once there is a conversion or any other operation on the column
the index is no longer good for seeks (as the index may not reflect
the resulting value of the operation).

What does "select databasepropertyex(db_name(), 'COllation')" return
for the two databases?

Could you run the query with SET STATISTICS PROFILE ON and post the result
from the Stmt column? (Turn of Execution Plan in QA)

[quoted text, click to view]

Which is the peferctly normal query plan for that query.


--
Erland Sommarskog, SQL Server MVP, sommar@algonet.se

Books Online for SQL Server SP3 at
Laurence Breeze
3/16/2004 5:44:15 PM
Hi Erland,


[quoted text, click to view]

SQL_Latin1_General_CP1_CI_AS is returned for both databases.

[quoted text, click to view]

I did this for the TEST machine (the one with the expected QEP and
response times) and the result was:

StmtText
--------
SELECT * FROM [lts_leu_table] WHERE [persid]=@1
|--Clustered Index
Seek(OBJECT:([arsystem_cato_acct].[dbo].[LTS_leu_table].[PK__LTS_leu_old__1EAF7B80]),
SEEK:([LTS_leu_table].[PersId]=Convert([@1])) ORDERED FORWARD)

I then repeated this for the LIVE machine (the one with the QEP problem
and poor response times) and got nothing. Looking at books online I
noticed that it seemed to do much the same as "SET SHOWPLAN ALL". I ran
this and did get some output:

StmtText
--------
select * from lts_leu_table where persid = '12345678'
|--Bookmark Lookup(BOOKMARK:([Bmk1000]),
OBJECT:([arsystem_cato].[dbo].[LTS_leu_table]))
|--Parallelism(Gather Streams)
|--Index
Scan(OBJECT:([arsystem_cato].[dbo].[LTS_leu_table].[PersId_ind]),
WHERE:(Convert([LTS_leu_table].[PersId])=Convert([@1])))

I can see that the CONVERT function is being used, I guess this is what
you meant by "auto-converted".


Interestingly, I also ran the following select statement against both
databases:

select * from lts_leu_table where persid like '12345678%'

The result in both cases was a QEP with a Clustered Index Seek.

I have to admit to being very confused.

Laurence Breeze

Erland Sommarskog
3/16/2004 8:28:36 PM
Laurence Breeze (laurence_breeze@yahoo.co.uk) writes:
[quoted text, click to view]

Is there any particular reason your tables have a different collation
that the default collation for the database? Although I don't really
see how (yet), I'm fairly confident that this is the source of your
problem.

[quoted text, click to view]

Yes, this confirms my suspicion. It is the Convert([LTS_leu_table].[PersId])
that is giving you the scan.

Now, did you ever post the version number for you two servers? You
retrieve this with SELECT @@version. If the production machine has
a number lower than 8.00.760 (which is SP3), upgrade this machine to
SP3. If it is higher (because thers is a hotfix it looks like its
bug-reporting time.



--
Erland Sommarskog, SQL Server MVP, sommar@algonet.se

Books Online for SQL Server SP3 at
Laurence Breeze
3/17/2004 5:01:55 PM


[quoted text, click to view]

This is probably a result of the create table statement being generated
originally on a developers machine. This is not uncommon where I work
and I hadn't realised that there may be problems with this.


[quoted text, click to view]

Test machine = 8.00.760
Prod machine = 8.00.679

We intend to upgrade this machine in the next daya or so. I'll let you
know the results of the upgrade on the queries.

Thanks

Laurence Breeze



Erland Sommarskog
3/17/2004 10:47:16 PM
Laurence Breeze (laurence_breeze@yahoo.co.uk) writes:
[quoted text, click to view]

There is not really include the collation in the column definitions -
unless you actually want to use a certain collation for some data.

There are a couple of places where you could run into trouble with
this. The most apparent is temp tables, since temp table uses the
default collation of the server. The effect you saw here is a bug,
but nevertheless local variables and string literals take their
collation from the database, so they have to been converting at a
cost of a few extra cycles.

A CREATE TABLE statement for production code should be written by
hand or generated from standalone database-design tool. Whatever
the code should be kept under source control.

[quoted text, click to view]

Seems we have found the reason, and that the issue has an obvious
resolution.


--
Erland Sommarskog, SQL Server MVP, sommar@algonet.se

Books Online for SQL Server SP3 at
Erland Sommarskog
3/18/2004 10:17:06 PM
Erland Sommarskog (sommar@algonet.se) writes:
[quoted text, click to view]

"There is not really any reason to include the collation..."

Sorry for the unintelligible sentence above.


--
Erland Sommarskog, SQL Server MVP, sommar@algonet.se

Books Online for SQL Server SP3 at
AddThis Social Bookmark Button