April 2, 2010 at 2:53 pm
I'm trying to find a way to improve the performance of a query that uses a between clause
Here's what the table being queried looks like...
CREATE TABLE [dbo].[EffectiveItems](
[ItemID] [int] NOT NULL, -- foreign key
[BEGIN] [int] NOT NULL,
[END] [int] NOT NULL,
[LOW] [int] NOT NULL,
[HIGH] [int] NOT NULL
)
This table has about 3 million rows and every row is unique. The LOW and HIGH columns are pretty selective (no more than 5 duplicate values). There are a lot of duplicates for the BEGIN, END and ItemID columns. This table is pretty much a read-only table. It gets updated about once every 3 months.
Here's the query I'm running...
select *
from EffectiveItems
where 982827279 between LOW and HIGH
This query takes about 300 ms and 12,000 Reads to complete. If I do a query like this...
select *
from EffectiveItems
where 982827273 = LOW
This query takes about 0 ms (doesn't even register) and 3 Reads to complete. I've tried clustered, non-clustered & covering indexes but all of the results are very similar.
What can be done to improve the performance the query using between?
Thanks!
April 2, 2010 at 3:58 pm
Please post your actual execution plans.
Jason...AKA CirqueDeSQLeil
_______________________________________________
I have given a name to my pain...MCM SQL Server, MVP
SQL RNNR
Posting Performance Based Questions - Gail Shaw[/url]
Learn Extended Events
April 2, 2010 at 4:09 pm
OK, here it is
StmtText
--------
Parallelism(Gather Streams)
|--Clustered Index Seek(OBJECT:([Database].[dbo].[EffectiveItems].[IX_EffectiveItems]), SEEK:([Database].[dbo].[EffectiveItems].[LOW] <= [@1]), WHERE:([@2]<=[Database].[dbo].[EffectiveItems].[HIGH]) ORDERED FORWARD)
April 2, 2010 at 4:11 pm
Is there any change to have a known maximum range between LOW and HIGH?
If so, you could base your query on the LOW column to narrow down the number of columns to be checked against the actual HIGH value.
Something like
WHERE LOW >= 982827279
AND LOW < 982827279 +1000
AND HIGH <= 982827279
April 2, 2010 at 4:13 pm
cgreathouse (4/2/2010)
OK, here it isStmtText
--------
Parallelism(Gather Streams)
|--Clustered Index Seek(OBJECT:([Database].[dbo].[EffectiveItems].[IX_EffectiveItems]), SEEK:([Database].[dbo].[EffectiveItems].[LOW] <= [@1]), WHERE:([@2]<=[Database].[dbo].[EffectiveItems].[HIGH]) ORDERED FORWARD)
Please post the xml as shown by Gail in the post listed in my signature.
Jason...AKA CirqueDeSQLeil
_______________________________________________
I have given a name to my pain...MCM SQL Server, MVP
SQL RNNR
Posting Performance Based Questions - Gail Shaw[/url]
Learn Extended Events
April 2, 2010 at 4:15 pm
lmu92 (4/2/2010)
Is there any change to have a known maximum range between LOW and HIGH?If so, you could base your query on the LOW column to narrow down the number of columns to be checked against the actual HIGH value.
Something like
WHERE LOW >= 982827279
AND LOW < 982827279 +1000
AND HIGH <= 982827279
Wouldn't that just return 1 value?
Jason...AKA CirqueDeSQLeil
_______________________________________________
I have given a name to my pain...MCM SQL Server, MVP
SQL RNNR
Posting Performance Based Questions - Gail Shaw[/url]
Learn Extended Events
April 2, 2010 at 4:19 pm
The 982827279 is just an example. The value changes and can be anywhere between 72 and 994039999
April 2, 2010 at 4:21 pm
Also, what are your indexes for that table?
Jason...AKA CirqueDeSQLeil
_______________________________________________
I have given a name to my pain...MCM SQL Server, MVP
SQL RNNR
Posting Performance Based Questions - Gail Shaw[/url]
Learn Extended Events
April 2, 2010 at 4:31 pm
Here's the plan in xml form
<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.0" Build="9.00.3042.00" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementCompId="0" StatementEstRows="226439" StatementId="1" StatementOptmLevel="FULL" StatementSubTreeCost="10.7897" StatementText="SELECT * FROM [EffectiveItems] WHERE @1>=[LOW] AND @2<=[HIGH]" StatementType="SELECT">
<StatementSetOptions ANSI_NULLS="false" ANSI_PADDING="false" ANSI_WARNINGS="false" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="false" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="false" />
<QueryPlan DegreeOfParallelism="2" CachedPlanSize="11" CompileTime="2" CompileCPU="2" CompileMemory="136">
<RelOp AvgRowSize="27" EstimateCPU="0.58435" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="226439" LogicalOp="Gather Streams" NodeId="0" Parallel="true" PhysicalOp="Parallelism" EstimatedTotalSubtreeCost="10.7897">
<OutputList>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="ItemID" />
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="BEGIN" />
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="END" />
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="LOW" />
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="HIGH" />
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="0" ActualRows="1" ActualEndOfScans="1" ActualExecutions="1" />
</RunTimeInformation>
<Parallelism>
<RelOp AvgRowSize="27" EstimateCPU="1.54559" EstimateIO="7.98535" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="226439" LogicalOp="Clustered Index Seek" NodeId="1" Parallel="true" PhysicalOp="Clustered Index Seek" EstimatedTotalSubtreeCost="9.53093">
<OutputList>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="ItemID" />
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="BEGIN" />
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="END" />
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="LOW" />
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="HIGH" />
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="2" ActualRows="1" ActualEndOfScans="1" ActualExecutions="1" />
<RunTimeCountersPerThread Thread="1" ActualRows="0" ActualEndOfScans="1" ActualExecutions="1" />
<RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="0" ActualExecutions="0" />
</RunTimeInformation>
<IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="ItemID" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="BEGIN" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="END" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="LOW" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="HIGH" />
</DefinedValue>
</DefinedValues>
<Object Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Index="[IX_EffectiveItems]" />
<SeekPredicates>
<SeekPredicate>
<EndRange ScanType="LE">
<RangeColumns>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="LOW" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="[@1]">
<Identifier>
<ColumnReference Column="@1" />
</Identifier>
</ScalarOperator>
</RangeExpressions>
</EndRange>
</SeekPredicate>
</SeekPredicates>
<Predicate>
<ScalarOperator ScalarString="[@2]<=[Database].[dbo].[EffectiveItems].[HIGH]">
<Compare CompareOp="LE">
<ScalarOperator>
<Identifier>
<ColumnReference Column="@2" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Database]" Schema="[dbo]" Table="[EffectiveItems]" Column="HIGH" />
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
</IndexScan>
</RelOp>
</Parallelism>
</RelOp>
<ParameterList>
<ColumnReference Column="@2" ParameterCompiledValue="(982827279)" ParameterRuntimeValue="(982827273)" />
<ColumnReference Column="@1" ParameterCompiledValue="(982827279)" ParameterRuntimeValue="(982827273)" />
</ParameterList>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>
April 2, 2010 at 4:35 pm
I've tried a number of different one. Here's what is currently being used
CREATE CLUSTERED INDEX [IX_EffectiveItems] ON [dbo].[EffectiveItems]
(
[LOW] ASC,
[HIGH] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
April 2, 2010 at 5:01 pm
CirquedeSQLeil (4/2/2010)
lmu92 (4/2/2010)
Is there any change to have a known maximum range between LOW and HIGH?If so, you could base your query on the LOW column to narrow down the number of columns to be checked against the actual HIGH value.
Something like
WHERE LOW >= 982827279
AND LOW < 982827279 +1000
AND HIGH <= 982827279
Wouldn't that just return 1 value?
It depends. 😀 It also may return 1000 rows...
What I tried to do is setting an upper limit to use the clustered index more efficient.
April 2, 2010 at 5:12 pm
lmu92 (4/2/2010)
CirquedeSQLeil (4/2/2010)
lmu92 (4/2/2010)
Is there any change to have a known maximum range between LOW and HIGH?If so, you could base your query on the LOW column to narrow down the number of columns to be checked against the actual HIGH value.
Something like
WHERE LOW >= 982827279
AND LOW < 982827279 +1000
AND HIGH <= 982827279
Wouldn't that just return 1 value?
It depends. 😀 It also may return 1000 rows...
What I tried to do is setting an upper limit to use the clustered index more efficient.
My concern is due to the ands
WHERE LOW >= 982827279
AND LOW < 982827279 +1000
AND HIGH <= 982827279
Since the High and Low are the same value - it won't matter what the middle And is doing so long as the Low is less than that end value
WHERE LOW >= 982827279
AND LOW < 982827279 +1000
AND HIGH <= 982827279
This essentially says High <= 982827279 <= LOW
But 982827279 should be > than the low value and < the high value.
Or LOW <= 982827279 <= HIGH
982827279 between LOW and HIGH.
Am I making sense - it is a flip-flop of the < >.
Jason...AKA CirqueDeSQLeil
_______________________________________________
I have given a name to my pain...MCM SQL Server, MVP
SQL RNNR
Posting Performance Based Questions - Gail Shaw[/url]
Learn Extended Events
April 2, 2010 at 5:26 pm
CirquedeSQLeil (4/2/2010)
...My concern is due to the ands
WHERE LOW >= 982827279
AND LOW < 982827279 +1000
AND HIGH <= 982827279
Since the High and Low are the same value - it won't matter what the middle And is doing so long as the Low is less than that end value
WHERE LOW >= 982827279
AND LOW < 982827279 +1000
AND HIGH <= 982827279
This essentially says High <= 982827279 <= LOW
But 982827279 should be > than the low value and < the high value.
Or LOW <= 982827279 <= HIGH
982827279 between LOW and HIGH.
Am I making sense - it is a flip-flop of the < >.
Oooopss!! Of course, you're right :blush:
Should have been
WHERE LOW <= 982827279
AND LOW > 982827279 -1000
AND HIGH >= 982827279
April 2, 2010 at 5:30 pm
That looks better.
Thanks Lutz
Jason...AKA CirqueDeSQLeil
_______________________________________________
I have given a name to my pain...MCM SQL Server, MVP
SQL RNNR
Posting Performance Based Questions - Gail Shaw[/url]
Learn Extended Events
April 2, 2010 at 5:40 pm
Thank you Jason for detecting my simple but most relevant mistake.
Since my error rate increase dramatically at the moment (in another thread I just forgot that table variables where introduced in SS2K). I think I'm taking some time off now... It's 1:40AM over here anyway...
Good night out there. Wherever you are.
Viewing 15 posts - 1 through 15 (of 31 total)
You must be logged in to reply to this topic. Login to reply