MattB
3/29/2007 10:50:00 PM
Thanks for the response!
If I created another indexed view on the table called vwGrouped_Messages
like you described, and then also created the clustered index on that view,
would I even have to worry about blocking since it's adding the index to a
view and not the actual Messages table, or would it still affect the Messages
table?
If I can create the view and index without affecting table blocking in our
production environment, I want to perform the existing query side-by-side the
new query, and use profiler to monitor the difference in reads/speed. What do
you think?
Thanks again for all the help. Oh, and here is the execution plan using the
SET SHOWPLAN_TEXT ON:
DECLARE @tmpFolders TABLE(
FolderID int
)
INSERT INTO @tmpFolders (FolderID) VALUES (-1)
(3 row(s) affected)
StmtText
------------------------------------------------------------------------------
|--Table Insert(OBJECT:(@tmpFolders), SET:(@tmpFolders.[FolderID]=-1))
(1 row(s) affected)
StmtText
---------------------------------------------------
INSERT INTO @tmpFolders (FolderID) VALUES (-2)
(1 row(s) affected)
StmtText
------------------------------------------------------------------------------
|--Table Insert(OBJECT:(@tmpFolders), SET:(@tmpFolders.[FolderID]=-2))
(1 row(s) affected)
StmtText
---------------------------------------------------
INSERT INTO @tmpFolders (FolderID) VALUES (-4)
(1 row(s) affected)
StmtText
------------------------------------------------------------------------------
|--Table Insert(OBJECT:(@tmpFolders), SET:(@tmpFolders.[FolderID]=-4))
(1 row(s) affected)
StmtText
---------------------------------------------------
INSERT INTO @tmpFolders (FolderID) VALUES (-5)
(1 row(s) affected)
StmtText
------------------------------------------------------------------------------
|--Table Insert(OBJECT:(@tmpFolders), SET:(@tmpFolders.[FolderID]=-5))
(1 row(s) affected)
StmtText
-----------------------------------------------------
INSERT INTO @tmpFolders (FolderID) VALUES (-7)
(1 row(s) affected)
StmtText
------------------------------------------------------------------------------
|--Table Insert(OBJECT:(@tmpFolders), SET:(@tmpFolders.[FolderID]=-7))
(1 row(s) affected)
StmtText
---------------------------------------------------------------------------------------------------------------------------------------------
INSERT INTO @tmpFolders (FolderID)
SELECT
FolderID
FROM
Folders WITH (NOLOCK)
WHERE
AccountGroupID = @intAccountGroupID
(1 row(s) affected)
StmtText
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|--Table Insert(OBJECT:(@tmpFolders),
SET:(@tmpFolders.[FolderID]=[Folders].[FolderID]))
|--Top(ROWCOUNT est 0)
|--Index
Seek(OBJECT:([ewMessageSvrData].[dbo].[Folders].[IX_Folders_AccountGroupID_FolderID]),
SEEK:([Folders].[AccountGroupID]=[@intAccountGroupID]) ORDERED FORWARD)
(3 row(s) affected)
StmtText
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SELECT
f.FolderID,
ISNULL(d.UnreadCount, 0) As UnreadCount
FROM
@tmpFolders f
LEFT OUTER JOIN (
SELECT
m.FolderID,
COUNT(*) As UnreadCount
FROM
vwLookup_Accounts a WITH (NOLOCK)
JOIN Messages m WITH (NOLOCK) ON m.Account
(1 row(s) affected)
StmtText
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|--Compute Scalar(DEFINE:([Expr1005]=isnull([Expr1003], 0)))
|--Nested Loops(Left Outer Join, OUTER
REFERENCES:([f].[FolderID]))
|--Sort(ORDER BY:([f].[FolderID] ASC))
| |--Table Scan(OBJECT:(@tmpFolders AS [f]))
|--Table Spool
|--Compute
Scalar(DEFINE:([Expr1003]=Convert([Expr1008])))
|--Stream Aggregate(DEFINE:([Expr1008]=Count(*)))
|--Nested Loops(Inner Join, OUTER
REFERENCES:([Accounts].[AccountID]) WITH PREFETCH)
|--Bookmark
Lookup(BOOKMARK:([Bmk1000]), OBJECT:([ewMessageSvrLookup].[dbo].[Accounts])
WITH PREFETCH)
| |--Index
Seek(OBJECT:([ewMessageSvrLookup].[dbo].[Accounts].[IX_Accounts]),
SEEK:([Accounts].[AccountGroupID]=[@intAccountGroupID]) ORDERED FORWARD)
|--Index
Seek(OBJECT:([ewMessageSvrData].[dbo].[Messages].[IX_Messages_AcctID_FolderID_Unread_MsgTypeID_MessageID]
AS [m]), SEEK:([m].[AccountID]=[Accounts].[AccountID] AND
[m].[FolderID]=[f].[FolderID] AND [m].[Unread]
(11 row(s) affected)
StmtText
----------
RETURN
(1 row(s) affected)
StmtText
-----------------------------
SET STATISTICS PROFILE OFF
(1 row(s) affected)
"Gert-Jan Strik" wrote:
> Matt,
>
> It looks like the original creator has put a quite a lot of thought in
> it. Your SP would not benefit from an added clustered index. The current
> nonclustered index is already covering the query.
>
> You did not specify the indexes of view vwLookup_Accounts. The query
> would benefit from an index on (AccountGroupID, AccountID). If this is
> not an indexed view, then the view's definition is relevant (and not yet
> posted). The actual query plan would be interesting too (check out SET
> SHOWPLAN_TEXT ON)
>
> The performance of the SP should depend on the amount of folders for the
> specified AccountGroup, and the number of accounts in the AccountGroup.
> However, the most determining factor is probably the massive number of
> "read" messages.
>
> You could try the query below, and see if it runs any faster.
>
> SELECT f.FolderID
> , ISNULL((
> SELECT COUNT(*)
> FROM vwLookup_Accounts a WITH (NOLOCK)
> JOIN Messages m WITH (NOLOCK)
> ON m.AccountID = a.AccountID
> WHERE a.AccountGroupID = @intAccountGroupID
> AND m.Unread in (1,2)
> AND m.FolderID = f.FolderID
> ),0) As UnreadCount
> FROM @tmpFolders f
>
> However, I expect that the current query is optimal. Have you checked
> whether there are AccountGroups that have several millions of messages
> (both read and unread)? An accountgroup with 4 million messages would
> explain the 20K reads. An accountgroup with 20 million messages would
> explain the 100K reads.
>
>
> Another approach is to consider an indexed view on the Messages table.
> If you are using Enterprise Edition, it might be sufficient to define
> the following view:
>
> CREATE VIEW dbo.vwGrouped_Messages WITH SCHEMABINDING
> AS
> SELECT AccountID, FolderID, COUNT_BIG(*) AS COUNT
> FROM dbo.Messages
> WHERE Unread IN (1,2)
> GROUP BY AccountID, FolderID;
>
> CREATE UNIQUE CLUSTERED INDEX PK_vwGrouped_Messages
> ON dbo.vwGrouped_Messages(AccountID, FolderID);
>
> If you using Standard Edition, or if the indexed view is not used
> automatically in your SP, then you could rewrite the query in the SP to:
>
> SELECT f.FolderID
> , ISNULL(d.UnreadCount, 0) As UnreadCount
> FROM @tmpFolders f
> LEFT OUTER JOIN (
> SELECT m.FolderID
> , SUM(COUNT) As UnreadCount
> FROM vwLookup_Accounts a WITH (NOLOCK)
> JOIN vwGrouped_Messages m WITH (NOEXPAND,NOLOCK)
> ON m.AccountID = a.AccountID
> WHERE a.AccountGroupID = @intAccountGroupID
> GROUP BY m.FolderID
> ) d ON d.FolderID = f.FolderID
>
> Please note that creating the index on the view will require quite some
> time during which you will have a lot of blocking. It will also require
> some additional disk space (perminently). When it is done,
> inserts/updates/deletes will have become somewhat more expensive and
> might encounter more blocking. On the other hand, selects for unread
> messages should be much much faster, because they require a lot less
> I/O.
>
> HTH,
> Gert-Jan
>