indexed views not being used by optimizer

Please run the code below in a scratch database.  On the last query, check the execution plan.  

Why does it do a full table scan instead of doing an index seek on the covering index of the view?

The goal is to do a case-insensitive search on the LastName column, which in my real base table is collation Latin1_General_BIN (the server default here), by way of an index seek, not a scan.  I thought I could do this by creating a view of the table and setting the collation on that field to SQL_Latin1_General_CP1_CI_AS in the view, and index this version of the column on the view.

Thanks
create table dbo.tTest(id int identity(1,1), LastName varchar(80) COLLATE Latin1_General_BIN, FirstName varchar(80) COLLATE Latin1_General_BIN) 
GO

create unique clustered index tx0 on dbo.tTest(id)
GO

insert into dbo.tTest select 'Doe','John'
insert into dbo.tTest select 'Doe','Jane'
insert into dbo.tTest select 'Bach','Hellen'
insert into dbo.tTest select 'Smith','Jane'
insert into dbo.tTest select 'Smith','Mike'
insert into dbo.tTest select 'Jones','Mike'
GO

create view dbo.vTest  WITH SCHEMABINDING  as
select id, (LastName COLLATE SQL_Latin1_General_CP1_CI_AS) as LastName, (FirstName COLLATE SQL_Latin1_General_CP1_CI_AS) as FirstName
from dbo.tTest
GO

create unique clustered index vx0 on dbo.vTest(id) 
GO

create unique index vx1 on dbo.vTest(LastName,FirstName,id)
GO

select id, FirstName, LastName
from dbo.vTest -- with (index=vx1)
where LastName  = 'BACH'  
GO

--  drop view dbo.vTest
--  truncate table dbo.tTest
--  drop table dbo.tTest
GO

-- END OF SCRIPT --

--------------------------------------------------------------------------------------------
/** FYI - I can achieve the overall goal by creating calculated fields on the base table instead of a view (see below), but I would rather get this to work in a view (above) **/
--------------------------------------------------------------------------------------------


create table dbo.tTest(
                        id int identity(1,1), 
                        LastName  varchar(80) COLLATE Latin1_General_BIN, 
                        FirstName varchar(80) COLLATE Latin1_General_BIN,
                        cLastName  as (LastName  COLLATE SQL_Latin1_General_CP1_CI_AS),
                        cFirstName as (FirstName COLLATE SQL_Latin1_General_CP1_CI_AS)
                      ) 
GO

create unique clustered index tx0 on dbo.tTest(id)
GO

insert into dbo.tTest select 'Doe','John'
insert into dbo.tTest select 'Doe','Jane'
insert into dbo.tTest select 'Bach','Hellen'
insert into dbo.tTest select 'Smith','Jane'
insert into dbo.tTest select 'Smith','Mike'
insert into dbo.tTest select 'Jones','Mike'
GO

create unique index tx1 on dbo.tTest(cLastName,cFirstName,id)
GO

select id, cFirstName, cLastName
from dbo.tTest
where cLastName  = 'BACH'

--  drop view dbo.vTest
--  truncate table dbo.tTest
--  drop table dbo.tTest
GO

Open in new window

LVL 33
knightEknightAsked:
Who is Participating?
 
lluddenConnect With a Mentor Commented:
What edition of SQL are you using?

This might be pertinent
Indexed views are a feature of all versions of SQL Server 2000 and SQL Server 2005. In the Developer and Enterprise editions of SQL Server 2000 and SQL Server 2005, the query processor can use an indexed view to solve queries that structurally match the view, even if they don't refer to the view by name. In other versions, you must reference the view by name and use the NOEXPAND hint on the view reference to query the contents of an indexed view.
0
 
Dave BaldwinFixer of ProblemsCommented:
I'm not sure and I can't run it right now but I would think that creating the view and changing the collation AND creating the compound index both Require the table to be completely scanned when they are created.  And your WHERE using on the LastName doesn't correspond to any index you have created.  You don't have an index on only LastName so it has to scan the table to find only that value.
0
 
knightEknightAuthor Commented:
The index vx1  is on LastName, FirstName, id, which is sufficient for the WHERE clause containing only LastName.  Further, an index on only LastName would require the engine to do the additional step of going back to the base table for the other selected columns - that is why it is better (in cases like this) to include all selected columns in the index.  Also, see the second part of the script above, which makes use of calculated fields on the base table.  The fact that this works as desired is evidence that in principle this can work.  I just don't understand why the optimizer won't make use of the view index -- even with a query hint telling it to do so!  Any help here?
0
What Kind of Coding Program is Right for You?

There are many ways to learn to code these days. From coding bootcamps like Flatiron School to online courses to totally free beginner resources. The best way to learn to code depends on many factors, but the most important one is you. See what course is best for you.

 
Dave BaldwinConnect With a Mentor Fixer of ProblemsCommented:
I found this  http://blogs.msdn.com/b/mssqlisv/archive/2008/09/26/designing-composite-indexes.aspx  and this  http://www.sql-server-performance.com/2007/composite-indexes/  .  The best answer is to make the collation case-insensitive in the first place and eliminate the extra operations.
0
 
knightEknightAuthor Commented:
Thanks, good article:  http://msdn.microsoft.com/en-us/library/dd171921%28v=sql.100%29.aspx

In this case, the NOEXPAND hint did the trick:

select id, FirstName, LastName
from dbo.vTest with (NOEXPAND)
where LastName  = 'BACH'  
0
 
knightEknightAuthor Commented:
Very sorry, I accepted the wrong solution.  I had it already working from lluden's suggestion.  Sorry Dave, I'll have to fix this...
0
 
knightEknightAuthor Commented:
... Dave posted just as I accepted, and I just went to the last post in the thread, thinking it was lluden's.  I've requested to have this thread re-opened so I can award the points appropriately.
0
 
Dave BaldwinFixer of ProblemsCommented:
That's ok, at least you got it working.
0
 
knightEknightAuthor Commented:
I haven't looked over your articles yet Dave, but I agree with the premise you posted - make the collation case insensitive from the beginning.  However, in this case we did not have that option.  I appreciate your contribution and I will read them over.
0
 
knightEknightAuthor Commented:
Thanks to both of you.
0
 
Dave BaldwinFixer of ProblemsCommented:
You're welcome.
0
 
knightEknightAuthor Commented:
FYI - another good article on this exact subject:  http://www.eggheadcafe.com/software/aspnet/33165884/indexed-views-optimizer-hints-and-noexpand.aspx

The basic conclusion is that if the optimizer decides it has an execution plan that is "good enough" for this query, it runs with it.  Since in my example there are only six rows (1 page) in the table, one plan is probably as good as the next.  It may be the case that if the table has a few million rows in it, the optimizer will choose to use the index of the View (which in this case would be optimal) rather than any index on the base table.

0
 
Dave BaldwinFixer of ProblemsCommented:
That makes sense to me.  The extra disk activity for the index may swamp the time to just scan the table with only 6 rows.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.