Improve Between performance

  • 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!

  • 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

  • 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)

  • 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



    Lutz
    A pessimist is an optimist with experience.

    How to get fast answers to your question[/url]
    How to post performance related questions[/url]
    Links for Tally Table [/url] , Cross Tabs [/url] and Dynamic Cross Tabs [/url], Delimited Split Function[/url]

  • cgreathouse (4/2/2010)


    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)

    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

  • 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

  • The 982827279 is just an example. The value changes and can be anywhere between 72 and 994039999

  • 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

  • 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>

  • 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]

  • 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.



    Lutz
    A pessimist is an optimist with experience.

    How to get fast answers to your question[/url]
    How to post performance related questions[/url]
    Links for Tally Table [/url] , Cross Tabs [/url] and Dynamic Cross Tabs [/url], Delimited Split Function[/url]

  • 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

  • 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



    Lutz
    A pessimist is an optimist with experience.

    How to get fast answers to your question[/url]
    How to post performance related questions[/url]
    Links for Tally Table [/url] , Cross Tabs [/url] and Dynamic Cross Tabs [/url], Delimited Split Function[/url]

  • 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

  • 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.



    Lutz
    A pessimist is an optimist with experience.

    How to get fast answers to your question[/url]
    How to post performance related questions[/url]
    Links for Tally Table [/url] , Cross Tabs [/url] and Dynamic Cross Tabs [/url], Delimited Split Function[/url]

Viewing 15 posts - 1 through 15 (of 31 total)

You must be logged in to reply to this topic. Login to reply