August 14, 2007 at 4:53 am
Hi,
I have a stored procedure that takes 30s to run:
CREATE PROCEDURE dbo.usp_get_untranslated_prompts_for_export
@LabelID int
AS
SELECT p.PromptID AS ID,
p.Original_Prompt AS ORIGINAL_VALUE,
p.Approved_Prompt AS VALUE
FROM tblPrompts p
inner join tblPromptLabels pl on p.PromptID = pl.PromptID
WHERE p.promptid not in (SELECT promptid FROM tblPromptTranslations)
and p.Approved = 1
and p.[Ignore] = 0
and pl.LabelID = @LabelID
then:
EXEC usp_get_untranslated_prompts_for_export 27
However, if I just run the SQL on its own, it takes less than 1 second, i.e.
declare @LabelID int
set @LabelID = 27
SELECT p.PromptID AS ID,
p.Original_Prompt AS ORIGINAL_VALUE,
p.Approved_Prompt AS VALUE
FROM tblPrompts p
inner join tblPromptLabels pl on p.PromptID = pl.PromptID
WHERE p.promptid not in (SELECT promptid FROM tblPromptTranslations)
and p.Approved = 1
and p.[Ignore] = 0
and pl.LabelID = @LabelID
For info, I've put the table definitions at the end of this posting. Also:
tblPrompts has 15167 rows
tblPromptTranslations has 244783 rows
tblPromptLabels has 162010 rows
The result of the query has 68 rows.
The SQL used on its own and in the SP is identical.
I've tried dropping and re-creating the SP, and I've tried sp_recompile on the SP, but this makes no difference.
In the Execution Plans, the SQL on its own uses Hash Matches and a Right Anti Semi Join, yet the SP uses Nested Loops. I can't find a way of copying the executions plans to copy in here though
Any ideas why the SP should run so slowly?
Thanks
Adam
Table Defs:
CREATE TABLE [tblPromptTranslations] (
[TranslationID] [int] NOT NULL ,
[PromptID] [int] NOT NULL ,
CONSTRAINT [PK_tblPromptTranslations] PRIMARY KEY CLUSTERED
(
[TranslationID],
[PromptID]
) WITH FILLFACTOR = 90 ON [PRIMARY] ,
CONSTRAINT [FK_tblPromptTranslations_tblPrompts] FOREIGN KEY
(
[PromptID]
) REFERENCES [tblPrompts] (
[PromptID]
) ON DELETE CASCADE ,
CONSTRAINT [FK_tblPromptTranslations_tblTranslations] FOREIGN KEY
(
[TranslationID]
) REFERENCES [tblTranslations] (
[TranslationID]
) ON DELETE CASCADE
) ON [PRIMARY]
GO
CREATE TABLE [tblPrompts] (
[PromptID] [int] IDENTITY (1, 1) NOT NULL ,
[Version] [int] NOT NULL ,
[PromptName] [varchar] (100) COLLATE Latin1_General_CI_AS NOT NULL ,
[ApplicationIdentifier] [varchar] (50) COLLATE Latin1_General_CI_AS NOT NULL ,
[ProductID] [int] NULL ,
[ChangeType] [varchar] (50) COLLATE Latin1_General_CI_AS NOT NULL ,
[DateAdded] [smalldatetime] NULL CONSTRAINT [DF_tblPrompts_DateAdded] DEFAULT (getdate()),
[Prompt_Options] [varchar] (50) COLLATE Latin1_General_CI_AS NULL ,
[CompanyID] [int] NULL ,
[Original_Prompt] [nvarchar] (1000) COLLATE Latin1_General_CI_AS NOT NULL ,
[Approved_Prompt] [nvarchar] (1000) COLLATE Latin1_General_CI_AS NULL ,
[Comment] [nvarchar] (1000) COLLATE Latin1_General_CI_AS NULL ,
[Description] [nvarchar] (1000) COLLATE Latin1_General_CI_AS NULL ,
[UIContext] [varchar] (50) COLLATE Latin1_General_CI_AS NULL ,
[Localisable] [bit] NULL CONSTRAINT [DF_tblPrompts_Localisable] DEFAULT (1),
[PixelLength] [int] NULL ,
[Buffered] [varchar] (50) COLLATE Latin1_General_CI_AS NULL ,
[Buffer_Length] [varchar] (100) COLLATE Latin1_General_CI_AS NULL ,
[Target] [varchar] (50) COLLATE Latin1_General_CI_AS NULL ,
[Approved] [bit] NOT NULL CONSTRAINT [DF_tblPrompts_Approved] DEFAULT (0),
[Ignore] [bit] NULL CONSTRAINT [DF_tblPrompts_Ignore] DEFAULT (0),
[Deleted] [bit] NOT NULL CONSTRAINT [DF_tblPrompts_Deleted] DEFAULT (0),
[DateDeleted] [smalldatetime] NULL ,
[UpdatedBy] [int] NULL ,
[DateUpdated] [smalldatetime] NULL ,
CONSTRAINT [PK_tblPrompts] PRIMARY KEY CLUSTERED
(
[PromptID]
) WITH FILLFACTOR = 90 ON [PRIMARY] ,
CONSTRAINT [FK_tblPrompts_tblCompanies] FOREIGN KEY
(
[CompanyID]
) REFERENCES [tblCompanies] (
[CompanyID]
),
CONSTRAINT [FK_tblPrompts_tblProducts] FOREIGN KEY
(
[ProductID]
) REFERENCES [tblProducts] (
[ProductID]
) ON DELETE CASCADE ,
CONSTRAINT [FK_tblPrompts_tblRegisteredUsers] FOREIGN KEY
(
[UpdatedBy]
) REFERENCES [tblRegisteredUsers] (
[RegisteredUserID]
)
) ON [PRIMARY]
GO
CREATE TABLE [tblPromptLabels] (
[PromptID] [int] NOT NULL ,
[LabelID] [int] NOT NULL ,
CONSTRAINT [PK_tblPromptLabels] PRIMARY KEY CLUSTERED
(
[PromptID],
[LabelID]
) WITH FILLFACTOR = 90 ON [PRIMARY] ,
CONSTRAINT [FK_tblPromptLabels_tblLabels] FOREIGN KEY
(
[LabelID]
) REFERENCES [tblLabels] (
[LabelID]
) ON DELETE CASCADE ,
CONSTRAINT [FK_tblPromptLabels_tblPrompts] FOREIGN KEY
(
[PromptID]
) REFERENCES [tblPrompts] (
[PromptID]
) ON DELETE CASCADE
) ON [PRIMARY]
GO
August 14, 2007 at 4:58 am
Did you try to compare execution plans?
_____________
Code for TallyGenerator
August 14, 2007 at 4:59 am
Yes, and they were different.
If you can tell me how I can copy and post them here, I will do!
Regards
Adam
August 14, 2007 at 5:07 am
Here are the traces from running the two queries, if it is of any help:
SQL on its own:
set @LabelID = 27 SQL:StmtCompleted0000
SELECT p.PromptID AS ID, p.Original_Prompt AS ORIGINAL_VALUE, p.Approved_Prompt AS VALUE FROM tblPrompts p inner join tblPromptLabels pl on p.PromptID = pl.PromptID WHERE p.promptid not in (SELECT promptid FROM tblPromptTranslations) and p.ApprovSQL:StmtCompleted1411412300
Running the SP:
SELECT p.PromptID AS ID, p.Original_Prompt AS ORIGINAL_VALUE, p.Approved_Prompt AS VALUE FROM tblPrompts p inner join tblPromptLabels pl on p.PromptID = pl.PromptID WHERE p.promptid not in (SELECT promptSP:StmtCompleted8065150
usp_get_untranslated_prompts_for_export 27SQL:StmtCompleted8065190
Regards
Adam
August 14, 2007 at 5:09 am
Try this little trick to see if it works for you (all changes/additions in bold):
CREATE PROCEDURE dbo.usp_get_untranslated_prompts_for_export
@Label_ID int
AS
DECLARE @LabelID int
SELECT @LabelID = @Label_ID
/* Rest of you code goes here */
August 14, 2007 at 5:18 am
Paul,
That works! Thank you 🙂
It makes no sense though - why should it make such a difference?
Thanks
Adam
August 14, 2007 at 5:22 am
As the execution plans were different between the sp and the native code, it is likely that the procedure was compiled some time ago and since then there has been a new index or statistics created/updated on the table - the native sql would compile when you executed it and used the new index/statistics.
Recompiling the procedure without any changes have had the same effect.
J
August 14, 2007 at 6:04 am
But I tried all that, as I said in my original post
I ran 'ALTER PROCEDURE', I ran sp_recompile, I even dropped and recreated the SP, and this didn't make a difference to the execution plan.
And now, just changing the name of the variable in the last line of the SELECT to the variable passed to the SP, instead of the new one declared within the SP causes the problem to come back!
Very Strange
Regards
Adam
August 14, 2007 at 6:05 am
Did you try DBCC FREEPROCCACHE?
_____________
Code for TallyGenerator
August 14, 2007 at 6:18 am
Yes, and it doesn't make a difference.
Adam
August 14, 2007 at 7:20 am
Adam, like you I have tried those same things but w/o much success.
Normally, what usually happens is that the SP works fine for a while and suddenly it stops working properly! There have been a number of threads in this forum describing the same situation, but I'm not sure that a definitive answer has been given as to its cause - though there are some work-arounds!
Another work-around is to use the OPTION (FORCE ORDER) hint in the select statement that is causing you some angst.
Paul
August 14, 2007 at 7:45 am
Paul,
The way it stopped working (even though it was previously fine) is another symptom I noticed.
After I fixed this SP, I found another with the same problem, although several others are still OK and making the changes you suggested didn't improve performance.
I have noticed though that the Execution Plan does change considerably when making this change, beyond the simple addition of another select statement.
As the two variables (@LabelID and @Label_ID) are both 'int', the EP should be just the same for both versions. This really does not make sense.
If I have some time, I may create a more detailed post and put it on one of the Microsoft USENET groups, and see if I can get a better response from Microsoft.
Otherwise, instability like this is a major black mark against what is otherwise a very competent and usable database.
Anyway, thanks again for your help.
Regards
Adam
August 14, 2007 at 8:28 am
I've seen similar things happen, when a proc running fine suddenly gets a new plan that's horrible for performance. Even in the middle of the day.
We've never gotten a good response from Microsoft. Typically rebooting the server has cleared things out and built a good plan, but that's not a solution.
The addition of the new select with a variable is interesting. I wonder if it's just creating a new plan and you get a new one for some reason. I'm assuming statistics up to date, indexes defragged, etc. This is a sudden problem, not a degradation over time, right?
August 14, 2007 at 8:46 am
Steve,
I wondered if it was just creating a new plan, but I tried removing the new variable, and it reverted back to the old Execution Plan, so it really is treating the SQL in the stored procedure differently when this new variable is used. The variable has to be used, not just declared and populated. If you do this, but still use the variable as passed to the SP, you get the old execution plan.
Yes, it is a sudden problem. There's also a Maintenance Plan that optimises the database and checks integrity that runs once a week, so there should be no problems here.
The server has been running a month. To reboot it though would require a change ticket so that's not a route I want to go down just yet (although I will be checking the SP after the next time it's rebooted!)
Regards
Adam
August 14, 2007 at 4:34 pm
Adam
Executing SET SHOWPLAN_TEXT ON will cause a text version of the execution plan to be generated. Just run it before your statement/procedure.
--------------------
Colt 45 - the original point and click interface
Viewing 15 posts - 1 through 15 (of 28 total)
You must be logged in to reply to this topic. Login to reply