Covering Index for a view.

  • Ok, I'm no DBA and use indexs in a limited capacity.

    However I recently introduced a view that is causing serious headaches in terms of performance. The view is rarely updated but read often.

    At the minute it comprises of 4 columns and there are no indexes.

    ParentId,CategoryId, LevelId, RowCount

    All columns are integers and there are no unique values in any of the columns.

    Typically I will be joining or filtering on either ParentId or CategoryId.

    I think what I need is a covering index so that the view itself is never run.

    What do I need in terms of indexes.

    I must have a clustered index but should this be my covering index for all columns?

    Or should there be a second nonclustered index?

  • The view contains a CTE and I just realised that prevents indexing.

    Therefore I need to optimise the underlying tables / view design.

    ALTER VIEW [dbo].[vw_Product_Category_Levels] WITH SCHEMABINDING

    AS

    WITH TopCat (ParentId,CategoryId, LevelId)

    AS

    (

    SELECT tbl1.CategoryId, tbl1.CategoryId, 1

    FROM dbo.vw_tbl_Product_Categories as tbl1

    WHERE tbl1.ParentID = 0 AND tbl1.CAtegoryType = 10

    UNION ALL

    SELECT TopCat.CategoryId, tbl1.CategoryId, TopCat.LevelId + 1

    FROM dbo.vw_tbl_Product_Categories as tbl1 INNER JOIN TopCat

    ON tbl1.ParentId = TopCat.ParentID

    )

    SELECT ParentId,CategoryId, LevelId FROM TopCat

  • You'll have to test things. There isn't a way to be sure what's best, but here are a few suggestions. Note that the "view" isn't run, but incorporated into the query plan of whatever query it is included in. Those queries will ultimately determine what indexes make the most sense.

    Is this querying another view? Without knowing that it's hard to tell

    However, indexes on:

    dbo.vw_tbl_Product_Categories - ParentId, CategoryType, CategoryID include (level) might help.

    You might reorder CategoryID in there, depending on what other queries you have

  • This view is querying underlying views however they are relatively simple.

    dbo.vw_tbl_Product_Categories equates to

    SELECT Column names

    FROM tbl_Product_Categories

    WHERE Deleted = 0

    PK / FK relatations are indexed.

    When I viewed the execution plan most the resource was on an index spool (eager). This is from the CTE (i think)

    Output :

    [HW_Shop_2].[dbo].[tbl_Product_Categories].CategoryId

    Prefix: [HW_Shop_2].[dbo].[tbl_Product_Categories].ParentId, [HW_Shop_2].[dbo].[tbl_Product_Categories].Deleted = Scalar Operator([Recr1007]), Scalar Operator((0))

    The other option I'm considering is creating a physical table and creating triggers to update it. This is then essentially my indexed CTE view.

  • The index spool is expensive (http://ask.sqlservercentral.com/questions/6268/what-is-index-spool%5Burl%5D%5B/url%5D)

    However I'm not sure what you're trying to do with the indexed CTE or using triggers to update a table. Is this queried often and ralely updated?

    You also want a wider index to help here, not more indexes. Just because PK/FKs are indexed, that doesn't help with this query. You might need a new index that includes other columns, like Deleted. I'm not sure why you'd hide that in a view here. I get that you might be reusing code, but nesting views usually leads to performance issues. Use that view for querying active products. Don't use it for building your other views.

  • Basically it's taking a hierarchy and representing it in a flatter view so I can query up and down it and do aggregations etc.

    It's a typical "CategoryId, ParentId, Name" type of table.

    which then gets flattened to something like :

    ToplevelID, CategoryId, CategoryLevel

    so if I wanted to query a particular category and all it's sub categories I would just query the TopLevelId and it would list out the categoryId's and the category level it is from.

    The reason for the other views is that the application uses non destructive deletes and then clears them out every 30 days. So any table that does this has a view which the application uses instead of the raw table.

    I decided to use these views instead of the table so I don't have to add the delete flag and that everything looks at the same version of the truth. If there are other variables that factor into the active records in a table it becomes a nightmare maintaining it. Especially if the application views change for some reason in the future.

  • Just so you know, nesting views adds considerably to the work the optimizer has to do to unpack the views to try to get to an optimal execution plan. When there is too much work, it will stop optimizing and simply take whatever plan it has available. This means you could be getting much worse performance than you should. I really strongly advocate against using nested views.

    ----------------------------------------------------The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood... Theodore RooseveltThe Scary DBAAuthor of: SQL Server 2017 Query Performance Tuning, 5th Edition and SQL Server Execution Plans, 3rd EditionProduct Evangelist for Red Gate Software

  • ok thanks, point taken, I will put it into future design considerations.

    what I managed to establish last night was that adding the deleted flag to the index I had for parentId got rid of the index spool and the related queries now seem quicker particularly where there are further joins.

    The deleted flag should probably factor in pretty much every index.

  • I wouldn't use a view for this. If I had to continue with an Adjaceny List, I'd create two UDF's as iTVF's (Inline Table Valued Functions) so I could pick the nodes I wanted to find the upline or downline for. Think of such functions as "views that will take parameters".

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.
    "Change is inevitable... change for the better is not".

    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)
    Intro to Tally Tables and Functions

  • thanks for the tip...

    "adjacency list" I knew it had a name!

Viewing 10 posts - 1 through 9 (of 9 total)

You must be logged in to reply to this topic. Login to reply