EXPLAIN EXTENDED

How to create fast database queries

Overlapping ranges: Oracle

Comments enabled. I *really* need your comment

Continuing the theme on overlapping ranges:

From Stack Overflow:

I have an event table that specifies a date range with start_date and end_date fields.

I have another date range, specified in code, that defines the current week as week_start and week_end.

I'd like to query all events for the week.

Today, let's see it in Oracle.

A quick reminder: we have two options to write a condition for such a query.

A more simple one:

start_date < @week_end AND end_date > @week_start

is, uh, more simple, but less efficient in SQL Server and MySQL.

A more complex one:


(start_date > @week_start AND start_date < @week_end)
OR
(@week_start BETWEEN start_date AND end_date)

is more index friendly.

Let's create a sample table and see how Oracle copes with these conditions:

CREATE TABLE t_event
(
id NUMBER(10) NOT NULL,
evt_start DATE NOT NULL,
evt_end DATE NOT NULL,
evt_value VARCHAR(400) NOT NULL
)
/
ALTER TABLE t_event
ADD CONSTRAINT pk_event_id PRIMARY KEY (id)
/
CREATE INDEX ix_event_start ON t_event (evt_start)
/
CREATE INDEX ix_event_end_start ON t_event (evt_end, evt_Start)
/
BEGIN
DBMS_RANDOM.seed(20090702);
END;
/
INSERT
INTO    t_event (id, evt_start, evt_end, evt_value)
SELECT  level,
TO_DATE('02.07.2009', 'dd.mm.yyyy') - level / 1440,
TO_DATE('02.07.2009', 'dd.mm.yyyy') - level / 1440 + FLOOR(DBMS_RANDOM.value() * 35) + 1,
RPAD('Value ' || level, 400, ' ')
FROM    dual
CONNECT BY
level &lt;= 1000000
/        
COMMIT
/

Now, let's query.

Oracle supports a nice SQL-92 operator, OVERLAPS, which allows for simple overlapping checking:

SELECT  SUM(LENGTH(evt_value))
FROM    t_event
WHERE   (evt_end, evt_start) OVERLAPS (TO_DATE('21.06.2009', 'dd.mm.yyyy'), TO_DATE('21.06.2009', 'dd.mm.yyyy') + 7)

SUM(LENGTH(EVT_VALUE))
14362000
1 row fetched in 0.0001s (8.8749s)
SELECT STATEMENT 
 SORT AGGREGATE
  TABLE ACCESS FULL, 20090702_events.T_EVENT

Unfortunately, it's not optimizable, and this query uses full table scan and runs for 8.87 seconds.

Let's use the second condition:

SELECT  SUM(LENGTH(evt_value))
FROM    t_event e
WHERE   (evt_start &gt; TO_DATE('21.06.2009', 'dd.mm.yyyy') AND evt_start &lt;= TO_DATE('21.06.2009', 'dd.mm.yyyy') + 7)
        OR (TO_DATE('21.06.2009', 'dd.mm.yyyy') BETWEEN evt_start AND evt_end)

SUM(LENGTH(EVT_VALUE))
14362400
1 row fetched in 0.0001s (0.0763s)
SELECT STATEMENT 
 SORT AGGREGATE
  CONCATENATION 
   TABLE ACCESS BY INDEX ROWID, 20090702_events.T_EVENT
    INDEX RANGE SCAN, 20090702_events.IX_EVENT_START
   TABLE ACCESS BY INDEX ROWID, 20090702_events.T_EVENT
    INDEX RANGE SCAN, 20090702_events.IX_EVENT_END_START

This one is much better: 0.07 seconds, 100 times as fast.

We see that Oracle benefits from using the more complex condition too, provided that the table is properly indexed.

Written by Quassnoi

July 2nd, 2009 at 11:00 pm

Posted in Oracle

Leave a Reply