July 18, 2007 at 8:58 am
Hi all,
The following script runs for 5 hours as is, but if the red text is removed, runs in only 45 seconds - any ideas?
Thanks, Jaybee.
SET NOCOUNT ON ;
Select DISTINCT (tblAccounts.accountID) ,
tblAccounts.accountFirstName ,
tblAccounts.accountLastName,
tblServicesTree.serviceName,
tblcommunicationsSent.SentTo
FROM dbo.fn_getValidSubscriptions() as vs
JOIN tblAccounts on tblAccounts.accountID = vs.accountID
JOIN tblServicesTree on tblServicesTree.serviceID = vs.serviceID
JOIN tblcommunicationsSent on tblcommunicationsSent.accountID = vs.accountID
WHERE tblAccounts.accountEnabled <> 0 AND tblAccounts.accountSubscriptionsEnabled <> 0
and tblAccounts.accountID <> 3159
AND tblAccounts.ntUserID NOT IN ('Domain1','Domain2','Domain3')
order by tblAccounts.accountLastname, tblAccounts.accountFirstname
July 18, 2007 at 9:13 am
It probably doesn't help that you are joining to a UDF, but check to be sure you have an index on the following fields since you are doing a join:
tblcommunicationsSent.accountID
Where is this query? Are you testing it in Query Analyzer or is this a stored proc?
If it's a stored proc and it still runs slow after the index is created, run sp_updatestats and try again.
July 18, 2007 at 9:13 am
May be data in table tblcommunicationsSent is exclusive locked? Is big table tblcommunicationsSent?
July 18, 2007 at 9:20 am
I guess that it is a 1 to Many between fn_getValidSubscriptions and tblcommunicationsSent.
The query is doing a distinct on tblCommunicationsSent.SentTo - and the number of Many in the relationship will affect how long the query runs. It have to run through many more rows to determine the distinct - I suspect it is an exponential increase as if you double the number of rows each row has to be checked against twice as many rows and there are twice as many rows to check so it takes 4 times longer.
J
July 18, 2007 at 9:35 am
Well, 'tblcommunicationsSent' has 5.5 million rows....
July 18, 2007 at 9:38 am
Might be the cause of the problem!
Any other way of getting the SentTo information?
J
July 18, 2007 at 9:43 am
Perhaps some way to get distinct accountID's from a nested select?
July 18, 2007 at 9:44 am
If tblcommunicationsSent has 5+ million rows then the culprit is the join against the UDF. It's going to be doing a table scan because the UDF doesn't have (can't have) any indexes.
What is that function doing? It may be better to integrate the function's selects into your query here so it will be joining to real tables.
July 18, 2007 at 9:47 am
Have you got some data/ddl you can share with us?
J
July 18, 2007 at 9:51 am
Load data into temp table without join to tblcommunicationsSent. After join temp table with tblcommunicationsSent
July 18, 2007 at 3:07 pm
You need clustered index on tblcommunicationsSent.accountID.
There may be other columns included into that index but accountID must be the first one.
_____________
Code for TallyGenerator
July 19, 2007 at 3:03 am
Trying it now....31 minutes and counting.
I should add that this is ad hoc reporting, so no Stored Proc. The exe plan states that a table scan on tblcommunicationsSent is costing 87% of the time, and an inner join to AccountsID another 12%. The time difference between the script with red entries, and without is 6 hours(minimum) to 46 seconds.
Here are the table/index structures
CREATE TABLE [dbo].[tblCommunicationsSent] (
[communicationID] [int] NOT NULL ,
[accountID] [int] NOT NULL ,
[sentThrough_communicationMedium_listItemID] [int] NOT NULL ,
[sentTo] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[successfullySent] [bit] NOT NULL ,
[updateInformationType_listItemID] [int] NOT NULL ,
[smscReference] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[comments] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[createdDateTime] [datetime] NOT NULL ,
[createdBy_accountID] [int] NOT NULL
) ON [PRIMARY]
GO
CREATE INDEX [IX_tblCommunicationsSent] ON [dbo].[tblCommunicationsSent]([communicationID]) WITH FILLFACTOR = 70 ON [PRIMARY]
GO
CREATE INDEX [IX_tblCommunicationsSent_1] ON [dbo].[tblCommunicationsSent]([communicationID], [sentThrough_communicationMedium_listItemID], [accountID], [updateInformationType_listItemID], [createdDateTime] DESC ) WITH FILLFACTOR = 70 ON [PRIMARY]
GO
CREATE INDEX [IX_tblCommunicationsSent_2] ON [dbo].[tblCommunicationsSent]([createdDateTime] DESC , [communicationID] DESC ) WITH FILLFACTOR = 70 ON [PRIMARY]
GO
CREATE TABLE [dbo].[tblAccounts] (
[accountID] [int] IDENTITY (1, 1) NOT NULL ,
[ntUserID] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[ntUserDomain] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[accountType_listItemID] [int] NOT NULL ,
[parent_accountID] [int] NOT NULL ,
[accountName] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[accountFirstName] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[accountLastName] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[accountEnabled] [bit] NOT NULL ,
[createdDateTime] [datetime] NOT NULL ,
[createdBy_accountID] [int] NOT NULL ,
[accountSubscriptionsEnabled] [bit] NOT NULL ,
[useHtmlOnEmails] [bit] NOT NULL ,
[lastModifiedDateTime] [datetime] NOT NULL ,
[lastModifiedBy_accountID] [int] NOT NULL
) ON [PRIMARY]
GO
CREATE UNIQUE INDEX [IX_tblAccounts] ON [dbo].[tblAccounts]([ntUserID], [ntUserDomain]) WITH FILLFACTOR = 70 ON [PRIMARY]
GO
CREATE INDEX [IX_tblAccounts_1] ON [dbo].[tblAccounts]([accountFirstName], [accountLastName], [accountType_listItemID]) WITH FILLFACTOR = 70 ON [PRIMARY]
GO
CREATE INDEX [IX_tblAccounts_2] ON [dbo].[tblAccounts]([parent_accountID]) WITH FILLFACTOR = 70 ON [PRIMARY]
GO
CREATE TABLE [dbo].[tblServicesTree] (
[serviceID] [int] IDENTITY (1, 1) NOT NULL ,
[serviceName] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[parentID] [int] NOT NULL ,
[serviceShortName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[enabled] [bit] NOT NULL ,
[createdDateTime] [datetime] NOT NULL ,
[createdBy_accountID] [int] NOT NULL ,
[lastModifiedDateTime] [datetime] NOT NULL ,
[lastModifiedBy_accountID] [int] NOT NULL
) ON [PRIMARY]
CREATE UNIQUE INDEX [IX_tblServicesTree] ON [dbo].[tblServicesTree]([parentID], [serviceID]) WITH FILLFACTOR = 70 ON [PRIMARY]
GO
CREATE UNIQUE INDEX [IX_tblServicesTree_1] ON [dbo].[tblServicesTree]([serviceName]) WITH FILLFACTOR = 70 ON [PRIMARY]
GO
July 19, 2007 at 3:19 am
It is doing a full table scan because there is no usable index. AccountId is part of one index but it is not the first column in the index and is not usable.
Putting an index on AccountId would probably help and if you can make it clustered then it would be even better. Stop your query and add the index if you can. No point in waiting 6 hours.
How ad hoc is this? If it is a one off (everything that starts as a one off soon becomes business as usual) then there is not much to be gained by adding the index as it may not be used for anything else and will slow down inserts into the table. If the query will be run several times then it is probably worth creating the index.
Creating a simple index will probably only take a few minutes and will lock the table. Clustering the index will take a lot longer as it will have to sort the table as well.
J
July 19, 2007 at 7:03 am
I am tempted to put a clustered index on the table but there's a time limit on all this; I'll wait for a code-based solution, or I'll try the Index Tuning Wizard for the first time (a new corner of SQL Server, yayyyy!!) and see if a N/C index will suffice.
Everything else takes time I don't have, because after today it's automatically another DBA's problem, management guard my time jealously, but for the sake of pride I'd like to lick it before then.
Jaybee.
July 19, 2007 at 7:35 am
Even if you add an index, it will likely still do a table scan since you are joining to a user defined function.
Viewing 15 posts - 1 through 14 (of 14 total)
You must be logged in to reply to this topic. Login to reply