June 5, 2007 at 9:31 am
Does anyone have a query that can reasonably consistently generate two different plans between estimated and actual? I'm trying to come up with an example and I just can't find one.
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 6, 2007 at 5:48 am
You could maybe force one if you have outdated statistics? Can't say I have ever noticed one though, but not like I been keeping an eye open for it...
June 6, 2007 at 6:18 am
I'm thinking I'll try dropping the statistics on a table and see what happens. I'm trying to get a different plan, not just subtle differences in the plan (different row counts for example).
Any one else have any guesses?
I'll post the results of dropping the statistics.
Thanks for the help.
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 6, 2007 at 6:41 am
OK. Weak theory completely destroyed by reality. You can't drop statistics created by indexes and the other statistics just don't seem to be affecting the plan much. More ideas needed.
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 6, 2007 at 9:43 am
Just tossing out ideas here mind you....
How about a query that has a where clause on an XML data type? Those seems to have the most messed up query plans I have ever seen....
June 6, 2007 at 9:59 am
Ha!
Well, I'm not going for messed up, but it's worth a shot. I'll let you know.
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 7, 2007 at 5:50 am
Not sure this is what you are looking for... but I don't think you find a more dramatic difference...
DECLARE @Counter INT --Just a loop counter
DECLARE @Bitbucket INT --Just a place to store data
DECLARE @StartTime DATETIME --Just to measure duration
--===== Do the WHILE loop test of counting to 1 million
PRINT 'WHILE LOOP TEST...'
SET @StartTime = GETDATE() --Start the duration timer
SET @Counter = 1
SET @Bitbucket = 0
WHILE @Counter <= 1000000
BEGIN
SELECT @Bitbucket = @Bitbucket + 1
SET @Counter = @Counter + 1
END
-- Display the stats
PRINT STR(@BitBucket) + ' The final count (just to show work was done)'
PRINT STR(DATEDIFF(ms,@StartTime,GETDATE())) + ' Duration in milliseconds'
PRINT REPLICATE('-',78)
It's one of the reasons I avoid loops
--Jeff Moden
Change is inevitable... Change for the better is not.
June 7, 2007 at 7:07 am
Two identical plans:
<
ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan" Version="1.0" Build="9.00.3042.00">
<
BatchSequence>
<
Batch>
<
Statements>
<
StmtSimple StatementText="DECLARE @Counter INT --Just a loop counter DECLARE @Bitbucket INT --Just a place to store data DECLARE @StartTime DATETIME --Just to measure duration --===== Do the WHILE loop test of counting to 1 million PRINT 'WHILE LOOP TEST...' " StatementId="1" StatementCompId="1" StatementType="PRINT" />
<
StmtSimple StatementText=" SET @StartTime = GETDATE() --Start the duration timer " StatementId="2" StatementCompId="2" StatementType="ASSIGN" />
<
StmtSimple StatementText=" SET @Counter = 1 " StatementId="3" StatementCompId="3" StatementType="ASSIGN" />
<
StmtSimple StatementText=" SET @Bitbucket = 0 " StatementId="4" StatementCompId="4" StatementType="ASSIGN" />
<
StmtCond StatementText=" WHILE @Counter <= 1000000 " StatementId="5" StatementCompId="5" StatementType="COND">
<
Condition />
<
Then>
<
Statements>
<
StmtSimple StatementText=" BEGIN SELECT @Bitbucket = @Bitbucket + 1 " StatementId="6" StatementCompId="6" StatementType="ASSIGN" />
<
StmtSimple StatementText=" SET @Counter = @Counter + 1 " StatementId="7" StatementCompId="7" StatementType="ASSIGN" />
</
Statements>
</
Then>
</
StmtCond>
<
StmtSimple StatementText=" END -- Display the stats PRINT STR(@BitBucket) + ' The final count (just to show work was done)' " StatementId="8" StatementCompId="10" StatementType="PRINT" />
<
StmtSimple StatementText=" PRINT STR(DATEDIFF(ms,@StartTime,GETDATE())) + ' Duration in milliseconds' " StatementId="9" StatementCompId="11" StatementType="PRINT" />
<
StmtSimple StatementText=" PRINT REPLICATE('-',78) " StatementId="10" StatementCompId="12" StatementType="PRINT" />
</
Statements>
</
Batch>
</
BatchSequence>
</
ShowPlanXML>
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 7, 2007 at 8:13 am
I'm a little spacy this morning (production problems overnight), so maybe I'm doing something stupid here.
In desperation to try to find an answer this question, I hit up a person and asked her. She suggested that having two batches, one that updates a ton of data and one that selects from that data will work. So, I fired up AdventureWorks. If run these two queries, estimated or actual, I get two different plans:
SELECT
[OrderQty], [CarrierTrackingNumber]
FROM [Sales].[SalesOrderDetail]
WHERE [ProductID] = 897
Gets an index seek and a key lookup joined by a loop join
SELECT [OrderQty], [CarrierTrackingNumber]
FROM [Sales].[SalesOrderDetail]
WHERE [ProductID] = 870
This gets a clustered index scan.
So, I wrote up this little bit of code to manipulate the data such that the query against 897 should result in a scan (I had hoped).
BEGIN
TRAN
INSERT INTO [Sales].[SpecialOfferProduct]
([SpecialOfferID],[ProductID])
VALUES (2,897)
INSERT
INTO [Sales].[SpecialOfferProduct]
([SpecialOfferID],[ProductID])
VALUES (3,897)
INSERT INTO [Sales].[SpecialOfferProduct]
([SpecialOfferID],[ProductID])
VALUES (4,897)
UPDATE
[Sales].[SalesOrderDetail]
SET [ProductID] = 897
WHERE [ProductID] = 870
SELECT
[OrderQty], [CarrierTrackingNumber]
FROM [Sales].[SalesOrderDetail]
WHERE [ProductID] = 897
--ROLLBACK TRAN
I've got the rollback there so that I can throw away the updates after I'm done. The execution plan for the select is the same even though instead of accessing two rows as it did in the original, it now access 4,000+ rows. The only other bit of data I can provide is that the queries are doing simple parameterization... Maybe I should mess with that a bit... I'll try it.
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 7, 2007 at 8:31 am
Crud. Now parameter sniffing is taking part. If I take away the constant values & substitute a parameter I get clustered scans for everything...
**^& %^*%&% *%&$^& $*^%& ##%#!
And I really mean that.
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 7, 2007 at 10:06 am
May I be as bold as asking why you are trying to do this? Just to prove that you should not trust the estimated query plan when you are working on optimizations?
June 7, 2007 at 10:21 am
Basically, yes. I'm working on something and rather than simply caution that differences could arise, I'd like to show one.
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 7, 2007 at 4:47 pm
Maybe in SQL Server 2005... in 2000 the estimated execution plan show "something" and the actual execution plan shows absolutely nothing.
--Jeff Moden
Change is inevitable... Change for the better is not.
June 8, 2007 at 4:29 am
It's funny you say that. The reason I posted an XML plan was that the graphical plans were acting all wonky with that script. There's a mystery I'm going to skip over.
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
June 8, 2007 at 8:55 am
With some help from a person, here's one that works (by works I mean we get two different plans).
IF
EXISTS ( SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[NewOrders]')
AND type in ( N'U' ) )
DROP TABLE [NewOrders]
GO
SELECT
*
INTO NewOrders
FROM Sales.SalesOrderDetail
GO
CREATE INDEX IX_NewOrders_ProductID on NewOrders ( ProductID )
GO
SET SHOWPLAN_XML ON
-- Estimated Plan
GO
SELECT [OrderQty]
,[CarrierTrackingNumber]
FROM NewOrders
WHERE [ProductID] = 897
GO
SET SHOWPLAN_XML OFF
GO
BEGIN TRAN
UPDATE NewOrders
SET [ProductID] = 897
WHERE [ProductID] between 800 and 900
GO
SET STATISTICS XML ON
-- Actual Plan
GO
SELECT [OrderQty]
,[CarrierTrackingNumber]
FROM NewOrders
WHERE [ProductID] = 897
ROLLBACK TRAN
GO
SET STATISTICS XML OFF
"The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
- Theodore Roosevelt
Author of:
SQL Server Execution Plans
SQL Server Query Performance Tuning
Viewing 15 posts - 1 through 15 (of 16 total)
You must be logged in to reply to this topic. Login to reply