Re: "between" vs. ">= and <="

From: Mark D Powell <Mark.Powell_at_eds.com>
Date: Fri, 14 Nov 2008 09:37:47 -0800 (PST)
Message-ID: <a8dbf8bb-5822-4bbb-a70c-526caabd5245@d42g2000prb.googlegroups.com>


On Nov 14, 11:16 am, sybrandb <sybra..._at_gmail.com> wrote:
> On 14 nov, 17:10, Robert Klemme <shortcut..._at_googlemail.com> wrote:
>
>
>
>
>
> > Hi,
>
> > a client of us requested to replace ">= and <=" with "between" with a
> > TIMESTAMP column. Unfortunately I am still waiting for his
> > justification...  In the meantime I thought I do some researching.
> > Personally I cannot think of any reason why one or the other should be
> > more efficient.
>
> > I did some simplistic testing but no difference visible. (Experimental
> > code at end.)
>
> > I even believe that I once had found a statement saying that there is
> > no difference performance wise.  Any pointers?
>
> > Kind regards
>
> > robert
>
> > SQL> show release
> > release 1002000400
> > SQL> set echo on
> > SQL> set pagesize 100 linesize 100
> > SQL> create table t1
> >   2  as
> >   3  select systimestamp + level as ts
> >   4  from dual
> >   5  connect by level <= 1000000
> >   6  /
>
> > Table created.
>
> > SQL> begin
> >   2    dbms_stats.gather_schema_stats(
> >   3      user,
> >   4      method_opt => 'FOR ALL COLUMNS SIZE AUTO',
> >   5      granularity => 'AUTO',
> >   6      cascade => true,
> >   7      options => 'GATHER',
> >   8      degree => null
> >   9    );
> >  10  end;
> >  11  /
>
> > PL/SQL procedure successfully completed.
>
> > SQL> set autotrace traceonly
> > SQL> select *
> >   2  from t1
> >   3  where ts between systimestamp + 10000 and systimestamp + 10001
> >   4  /
>
> > Execution Plan
> > ----------------------------------------------------------
> > Plan hash value:
> > 3332582666
>
> > ---------------------------------------------------------------------------
> > | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)|
> > Time     |
> > ---------------------------------------------------------------------------
> > |   0 | SELECT STATEMENT   |      |  2495 | 19960 |   595  (58)|
> > 00:00:03 |
> > |*  1 |  FILTER            |      |       |       |
> > |          |
> > |*  2 |   TABLE ACCESS FULL| T1   |  2495 | 19960 |   595  (58)|
> > 00:00:03 |
> > ---------------------------------------------------------------------------
>
> > Predicate Information (identified by operation
> > id):
> > ---------------------------------------------------
>
> >    1 - filter(SYSTIMESTAMP(6)+10000<=SYSTIMESTAMP
> > (6)+10001)
> >    2 - filter("TS">=SYSTIMESTAMP(6)+10000
> > AND
> >               "TS"<=SYSTIMESTAMP
> > (6)+10001)
>
> > Statistics
> > ----------------------------------------------------------
> >           1  recursive
> > calls
> >           0  db block
> > gets
> >        1805  consistent
> > gets
> >           0  physical
> > reads
> >           0  redo
> > size
> >         334  bytes sent via SQL*Net to
> > client
> >         377  bytes received via SQL*Net from
> > client
> >           2  SQL*Net roundtrips to/from
> > client
> >           0  sorts
> > (memory)
> >           0  sorts
> > (disk)
> >           1  rows
> > processed
>
> > SQL> select *
> >   2  from t1
> >   3  where ts >= systimestamp + 10000 and ts <= systimestamp + 10001
> >   4  /
>
> > Execution Plan
> > ----------------------------------------------------------
> > Plan hash value:
> > 3332582666
>
> > ---------------------------------------------------------------------------
> > | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)|
> > Time     |
> > ---------------------------------------------------------------------------
> > |   0 | SELECT STATEMENT   |      |  2495 | 19960 |   595  (58)|
> > 00:00:03 |
> > |*  1 |  FILTER            |      |       |       |
> > |          |
> > |*  2 |   TABLE ACCESS FULL| T1   |  2495 | 19960 |   595  (58)|
> > 00:00:03 |
> > ---------------------------------------------------------------------------
>
> > Predicate Information (identified by operation
> > id):
> > ---------------------------------------------------
>
> >    1 - filter(SYSTIMESTAMP(6)+10000<=SYSTIMESTAMP
> > (6)+10001)
> >    2 - filter("TS">=SYSTIMESTAMP(6)+10000
> > AND
> >               "TS"<=SYSTIMESTAMP
> > (6)+10001)
>
> > Statistics
> > ----------------------------------------------------------
> >           1  recursive
> > calls
> >           0  db block
> > gets
> >        1805  consistent
> > gets
> >           0  physical
> > reads
> >           0  redo
> > size
> >         334  bytes sent via SQL*Net to
> > client
> >         377  bytes received via SQL*Net from
> > client
> >           2  SQL*Net roundtrips to/from
> > client
> >           0  sorts
> > (memory)
> >           0  sorts
> > (disk)
> >           1  rows
> > processed
>
> > SQL> create index i1 on t1(ts)
> >   2  /
>
> > Index created.
>
> > SQL> begin
> >   2    dbms_stats.gather_schema_stats(
> >   3      user,
> >   4      method_opt => 'FOR ALL COLUMNS SIZE AUTO',
> >   5      granularity => 'AUTO',
> >   6      cascade => true,
> >   7      options => 'GATHER',
> >   8      degree => null
> >   9    );
> >  10  end;
> >  11  /
>
> > PL/SQL procedure successfully completed.
>
> > SQL> select *
> >   2  from t1
> >   3  where ts between systimestamp + 10000 and systimestamp + 10001
> >   4  /
>
> > Execution Plan
> > ----------------------------------------------------------
> > Plan hash value:
> > 2069189151
>
> > --------------------------------------------------------------------------
> > | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)|
> > Time     |
> > --------------------------------------------------------------------------
> > |   0 | SELECT STATEMENT  |      |  2505 | 20040 |    14   (0)|
> > 00:00:01 |
> > |*  1 |  FILTER           |      |       |       |
> > |          |
> > |*  2 |   INDEX RANGE SCAN| I1   |  2505 | 20040 |    14   (0)|
> > 00:00:01 |
> > --------------------------------------------------------------------------
>
> > Predicate Information (identified by operation
> > id):
> > ---------------------------------------------------
>
> >    1 - filter(SYSTIMESTAMP(6)+10000<=SYSTIMESTAMP
> > (6)+10001)
> >    2 - access("TS">=SYSTIMESTAMP(6)+10000
> > AND
> >               "TS"<=SYSTIMESTAMP
> > (6)+10001)
>
> > Statistics
> > ----------------------------------------------------------
> >           1  recursive
> > calls
> >           0  db block
> > gets
> >           4  consistent
> > gets
> >           0  physical
> > reads
> >           0  redo
> > size
> >         334  bytes sent via SQL*Net to
> > client
> >         377  bytes received via SQL*Net from
> > client
> >           2  SQL*Net roundtrips to/from
> > client
> >           0  sorts
> > (memory)
> >           0  sorts
> > (disk)
> >           1  rows
> > processed
>
> > SQL> select *
> >   2  from t1
> >   3  where ts >= systimestamp + 10000 and ts <= systimestamp + 10001
> >   4  /
>
> > Execution Plan
> > ----------------------------------------------------------
> > Plan hash value:
> > 2069189151
>
> > --------------------------------------------------------------------------
> > | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)|
> > Time     |
> > --------------------------------------------------------------------------
> > |   0 | SELECT STATEMENT  |      |  2505 | 20040 |    14   (0)|
> > 00:00:01 |
> > |*  1 |  FILTER           |      |       |       |
> > |          |
> > |*  2 |   INDEX RANGE SCAN| I1   |  2505 | 20040 |    14   (0)|
> > 00:00:01 |
> > --------------------------------------------------------------------------
>
> > Predicate Information (identified by operation
> > id):
> > ---------------------------------------------------
>
> >    1 - filter(SYSTIMESTAMP(6)+10000<=SYSTIMESTAMP
> > (6)+10001)
> >    2 - access("TS">=SYSTIMESTAMP(6)+10000
> > AND
> >               "TS"<=SYSTIMESTAMP
> > (6)+10001)
>
> > Statistics
> > ----------------------------------------------------------
> >           1  recursive
> > calls
> >           0  db block
> > gets
> >           4  consistent
> > gets
> >           0  physical
> > reads
> >           0  redo
> > size
> >         334  bytes sent via SQL*Net to
> > client
> >         377  bytes received via SQL*Net from
> > client
> >           2  SQL*Net roundtrips to/from
> > client
> >           0  sorts
> > (memory)
> >           0  sorts
> > (disk)
> >           1  rows
> > processed
>
> > SQL> drop table t1
> >   2  /
>
> > Table dropped.
>
> > SQL> spool off
>
> Robert,
>
> The request may be sensible.
> In the 'old' days I had
>
> where a=... and date_column>= and date_column<= and c=...
>
> seeing optimized as
> where (a= and date_column>=) and (datum_column<=) and c=
>
> And gone was the index usage!
> Between ... and  ... is treated as one construct.
>
> --
> Sybrand Bakker
> Senior Oracle DBA- Hide quoted text -
>
> - Show quoted text -

The best way to make a decision is probably to run the same SQL with one version using >= and <= and the other using between on a large body of data several times averaging the run times of the code and then compare the average for the two versions.

Unless a noticable difference can be found I would suggest suggesting leaving the existing code alone and publishing a note to use between in new code.

In the 'old' days (7.0, 7.1) I am pretty sure I read that the CBO transformed a between into >= and <= statements. Somewhere along the line I believe that changed and between probably has been optimized. I have never tested it like I suggested above. I will have to add that to my list.

HTH -- Mark D Powell -- Received on Fri Nov 14 2008 - 11:37:47 CST

Original text of this message