Вопрос

SQL Fiddle: http://sqlfiddle.com/#!6/d4496/1 (data is pre-generated for your experiments)

There is obvious table:

CREATE TABLE Entity 
(
  ID int,
  Classificator1ID int,
  Classificator2ID int,
  Classificator3ID int,
  Classificator4ID int,
  Classificator5ID int
);

and the view :

CREATE VIEW dbo.EntityView (ID, Code1, Code2, Code3, Code4, Code5) 
WITH SCHEMABINDING

where entities fields Classificator1ID..Classificator5ID resolved to classificators values Code1..Code5

and there are a lot of indexes on this view:

CREATE UNIQUE CLUSTERED INDEX [IXUC_EntityView$ID] ON EntityView
  ([ID]);
CREATE UNIQUE NONCLUSTERED  INDEX [IXU_EntityView$ID$include$ALL] ON EntityView
  ([ID]) INCLUDE (Code1, Code2, Code3, Code4,  Code5);
CREATE UNIQUE NONCLUSTERED  INDEX [IXU_EntityView$ALL] ON EntityView
  ([ID],Code1, Code2, Code3, Code4,  Code5);  
CREATE UNIQUE NONCLUSTERED  INDEX [IXU_EntityView$ID$Code1] ON EntityView
  ([ID],Code1);
CREATE UNIQUE NONCLUSTERED  INDEX [IXU_EntityView$ID$include$Code1] ON EntityView
  ([ID])INCLUDE (Code1);
CREATE NONCLUSTERED  INDEX [IX_EntityView$Code1] ON EntityView
  (Code1);
CREATE NONCLUSTERED  INDEX [IX_EntityView$Code1$include$ID] ON EntityView
  (Code1) INCLUDE (ID);

But QO never use them! Try this:

SELECT * FROM EntityView;

SELECT ID, Code1 FROM EntityView;

SELECT ID, Code1, Code2, Code3, Code4, Code5 FROM EntityView;

SELECT ID, Code1, Code2, Code3, Code4, Code5 FROM EntityView WHERE ID=1;

SELECT ID, Code1 FROM EntityView Where Code1 like 'NR%';

Why? And especially What is wrong with "include" indexes? index created , has all fields and still unused...

ADDED: THIS IS JUST TEST! Please do not be so angry and do not push me to analyze those indexes maitinence problems.

In my real project I can't explain why QO ignores indexed views (very-very usefull indexed views). But sometimes I see it utilize them in other places. I have created this db snippet to experiment with index formulas but may be I should do something more: tune statistcs somehow ?

Это было полезно?

Решение

Running on 2012 Developer Edition the unhinted query is costed at approx 8 times more than the hinted query

enter image description here

Whilst a factor of 8 might sound a lot your example data is pretty small and the cost for selecting directly from the base tables is 0.0267122 vs 0.003293 for the estimated cost from the view.

Paul White explains in his answer here that automatic indexed view matching won't even be considered if a low enough plan is found first.

Artificially bumping up the costs for all the tables involved

UPDATE STATISTICS Classificator1 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Classificator2 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Classificator3 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Classificator4 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Classificator5 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Entity         WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 

Increases the cost of the base table plan to 29122.6

You should now see the view being matched (on Enterprise/Developer/Evaluation editions) unless you explicitly hint otherwise.

SELECT * FROM EntityView;

SELECT * FROM EntityView OPTION (EXPAND VIEWS) 

enter image description here

Другие советы

tl;dr answer: If you don't specify NOEXPAND, the query optimizer has no idea you are submitting a simple select from a view. It would have to match the expansion of your query (which is all it sees) with some view index. Probably won't bother when it's a five-way join with a bunch of casts.

View index matching to a query is a hard problem, and I believe your view is too complicated for the query engine to match to an index. Consider this one of your queries:

SELECT ID, Code1 FROM EntityView Where Code1 > 'NR%';

It's obvious to you that this can use a view index, but this is not the query the query engine sees. Views are automatically expanded if you don't specify NOEXPAND, so this is what goes to the query engine:

SELECT ID, Code1 FROM (
    SELECT e.ID, 'NR'+CAST(c1.CODE as nvarchar(11)) as Code1, 'NR'+CAST(c2.CODE as nvarchar(11)) as Code2, 'NR'+CAST(c3.CODE as nvarchar(11)) as Code3, 'NR'+CAST(c4.CODE as nvarchar(11)) as Code4, 'NR'+CAST(c5.CODE as nvarchar(11)) as Code5
    FROM dbo.Entity e
        inner join  dbo.Classificator1 c1 on e.ID = c1.ID
        inner join  dbo.Classificator2 c2 on e.ID = c2.ID
        inner join  dbo.Classificator3 c3 on e.ID = c3.ID
        inner join  dbo.Classificator4 c4 on e.ID = c4.ID
        inner join  dbo.Classificator5 c5 on e.ID = c5.ID;
) AS V;

The query engine sees this complicated query, and it has information (but probably not SQL of view definitions) that describe view indexes that have been defined. Given that this query and the view indexes both have multiple joins and casts, matching is a hard job.

Keep in mind that you know the joins and matches are identical in this query and the view indexes, but the query processor doesn't know that. It treats this query just the same as if it joined five copies of Classificator3, or if one of the columns was 'NQ'+CAST(c2.CODE as varchar(12)). The view index matcher (assuming it made any attempt to match this complicated a query) would have to match every detail of this query to the details of view indexes on the tables involved.

The query engine has as its #1 goal to figure out a way to execute the query efficiently. It's probably not designed to spend a lot of time trying to match every detail of a five-way join and CASTs to a view index.

If I had to guess, I suspect the view index matcher sees that the result columns of the query are not even columns of any underlying table (because of the CAST) and simply doesn't bother trying anything. Added: I'm wrong. I just tried Martin's suggestion of updating statistics to make the query expensive, and a view index was matched for some of these queries without NOEXPAND. The view matcher is cleverer than I thought! So the issue is that the view matcher probably tries harder to match a complicated query if its cost is very high.

Use the NOEXPAND hint instead of expecting the query engine to be able to figure out what matches here. NOEXPAND is absolutely your friend, because then the query engine gets to see

SELECT ID, Code1 FROM EntityView Where Code1 > 'NR%';

and it's then immediately obvious to the view index matcher that there is a useful index.

(Note: Your SQL Fiddle code has all 5 foreign key references to the same table, which is probably not what you want.)

Use the WITH (NOExpand) hint if you are on SQL Server Enterprise

Your query would be SELECT * FROM EntityView with (noexpand)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top