I have a CTE query that seems to be causing an awful lot of logical reads (or perhaps it doesnt). I have run SQL Server Profiler traces, and this query seems to be causing consistently one of the longest running queries. (it does get called with every page hit)
Basically I’m wanting to know if my CTE is optimised correctly, or can be improved.
SET STATISTICS IO ON;
GO
;WITH cte (PageId, PageTitle, PageType, PageHeadingId, ParentPage, InNavigation, OrgLevel, SortKey, PageOrder, PathLength, PathName, Active) AS
(
SELECT
PageId,
PageTitle,
PageType,
PageHeadingId,
ParentPage,
InNavigation,
0,
CAST (PageOrder AS VARBINARY(200)),
PageOrder,
0 AS PathLength,
CAST('' as varchar(300)) AS PathName,
Active
FROM dbo.ContentPage
WHERE ParentPage = 0
AND InNavigation = 1
UNION ALL
SELECT
b.PageId,
b.PageTitle,
b.PageType,
b.PageHeadingId,
b.ParentPage,
b.InNavigation,
cte.OrgLevel+1,
CAST(cte.SortKey + CAST (b.PageOrder AS BINARY(4)) AS VARBINARY(200)),
b.PageOrder,
((cte.OrgLevel+1) + len('....'+b.PageTitle)) as PathLength,
CAST ((cte.PathName+'....') AS VARCHAR(300)) AS PathName,
b.Active
FROM dbo.ContentPage b
JOIN cte ON b.ParentPage = cte.PageId
WHERE b.PageType NOT IN (4, 8, 11, 12, 14)
-- Remove specific page types from the ContentPage table
)
SELECT *, (PathName+PageTitle) AS Hierarchy
FROM cte WHERE InNavigation = 1
ORDER BY SortKey--, PageOrder
SET STATISTICS IO OFF;
GO
If I leave this line :
WHERE b.PageType NOT IN (4, 8, 11, 12, 14)
out, then the number of logical reads jumps from ~8500 to ~13000
(what the query does is builds a drop-down menu hierarchy in ASP.NET) If the logical reads are okay, then I assume I’ll have to come up with another way to cache/store this menu (that gets updated 2 or 3 times a week)
thanks
Table structure for ContentPage
CREATE TABLE [dbo].[ContentPage](
[PageId] [int] IDENTITY(1,1) NOT NULL,
[PageTitle] [nvarchar](150) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[PageQuote] [nvarchar](400) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[MetaKeywords] [nvarchar](200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[MetaDescription] [ntext] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[PageContent] [ntext] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Active] [bit] NOT NULL CONSTRAINT [DF_ContentPage_Active] DEFAULT ((0)),
[InNavigation] [bit] NOT NULL CONSTRAINT [DF_ContentPage_InNavigation] DEFAULT ((1)),
[PageOrder] [int] NOT NULL CONSTRAINT [DF_ContentPage_PageOrder] DEFAULT ((50)),
[ParentPage] [int] NOT NULL CONSTRAINT [DF_ContentPage_ParentPage] DEFAULT ((0)),
[PageType] [int] NOT NULL CONSTRAINT [DF_ContentPage_PageType] DEFAULT ((1)),
[CreatedBy] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[CreatedOn] [datetime] NULL,
[ModifiedBy] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[ModifiedOn] [datetime] NULL,
[PageViews] [int] NOT NULL CONSTRAINT [DF_ContentPage_PageViews] DEFAULT ((0)),
[Emailed] [int] NOT NULL CONSTRAINT [DF_ContentPage_Emailed] DEFAULT ((0)),
[Emailable] [bit] NOT NULL CONSTRAINT [DF_ContentPage_Emailable] DEFAULT ((1)),
[Printable] [bit] NOT NULL CONSTRAINT [DF_ContentPage_Printable] DEFAULT ((1)),
[ContactButton] [bit] NOT NULL CONSTRAINT [DF_ContentPage_PDFable] DEFAULT ((0)),
[PageHeadingId] [int] NULL CONSTRAINT [DF_ContentPage_PadeHeadingId] DEFAULT ((0)),
[AlternativeTitle] [nvarchar](150) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[RighthandImage] [nvarchar](250) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[IsMicrosite] [bit] NOT NULL CONSTRAINT [DF_ContentPage_IsMicrosite] DEFAULT ((0)),
CONSTRAINT [PK_ContentPage] PRIMARY KEY CLUSTERED
(
[PageId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Your CTE uses internal JOIN back to itself – so there is sort of a foreign key relationship here:
So having an index on
b.ParentPageand another one onPageIdmight be helpful.Also, your query has
WHEREclauses onb.PageTypeandInNavigation, so you should consider an index onPageType(or possibly(PageType, InNavigation)combined).Furthermore, you’re sorting by
SortKeywhich is based onPageOrderso an index on that might also be useful.Try to create one index at a time, re-run your query, compare your stats and numbers – and then decide which ones really benefit you (and which don’t). Indexing isn’t an exact science – you can’t always predict what helps and works and what not – you gotta try it and compare to your original query and then decide what helps and what doesn’t.