Oracle 12c SQL: Date Ranges

25
Date Ranges: Time for Oracle 12c SQL "Ask not what Oracle can do for you, ask what you can do with Oracle." Stew Ashton 2.0 UKOUG Tech 15 Stew ASHTON UKOUG Tech 14

Transcript of Oracle 12c SQL: Date Ranges

Page 1: Oracle 12c SQL: Date Ranges

Date Ranges:Time for Oracle 12c SQL

"Ask not what Oracle can do for you,ask what you can do with Oracle."

Stew Ashton 2.0 UKOUG Tech 15 Stew ASHTONUKOUG Tech 14

Page 2: Oracle 12c SQL: Date Ranges

2

Agenda

• Who am I?• Date/time data types• Date range concepts• 12c Temporal Validity• Date range DDL• Date range queries

Page 3: Oracle 12c SQL: Date Ranges

3

Who am I?• 35 years as Developer / Technical Architect

– Aeronautics, IBM, Finance– Mainframe, client-server, Web apps

• 27 years as an American in Paris• 10 years using Oracle database

– Performance analysis– Replace Java with SQL

• 3 years as internal "Oracle Development Expert"• "Career 2.0" started last week

Page 4: Oracle 12c SQL: Date Ranges

4

Review of date/time datatypes

• There is no "date" type: always a time element– Closest thing to a date: dte DATE CHECK(dte = TRUNC(dte))

• There are no "formats"• Values from 4712-01-01 00:00:00 BC through 9999-12-31 23:59:59 AD• Any of the above can be used in ranges

Datatype Year Month Day Hour Minute Second Fraction Time zoneDate Y Y Y Y Y Y

Timestamp Y Y Y Y Y Y Y Timestamp with local time zone Y Y Y Y Y Y Y implicit

Timestamp with time zone Y Y Y Y Y Y Y explicit

Page 5: Oracle 12c SQL: Date Ranges

5

Date/time Ranges• Use same data_type for start and end (duh!)• To NULL or not to NULL?

– SQL:2011 and 12c allow– Makes NULL mean something– Accounting for NULLs complicates queries and index design– Alternative: extreme values such as DATE '9999-21-31'

• "Start" is included in the range, "end" is not– Works for date/time ranges, not just dates– See SQL:2011 standard, 12c Temporal Validity– Allows ranges to "meet"– SQL can't use BETWEEN "start" and "end"

Page 6: Oracle 12c SQL: Date Ranges

6

Allen’sTime Interval

Arithmetic

Day of month 1 2 3 4A precedes B 1 2

B preceded by A 3 4A meets B 1 2 B met by A 2 3

A overlaps B 1 3B overlapped by A 2 4

A finished by B 1 3B finishes A 2 3A contains B 1 4B during A 2 3A starts B 1 2

B started by A 1 3A equals B 1 2 B equals A 1 2

Meet

Gap

"Overlap"

Day of month 1 2 3 4Day of month 1 2 3 4A precedes B 1 2

B preceded by A 3 4

Day of month 1 2 3 4A precedes B 1 2

B preceded by A 3 4A meets B 1 2 B met by A 2 3

Page 7: Oracle 12c SQL: Date Ranges

7

Temporal Validity: SQL:2011, 12c • Two temporal concepts

– "Transaction time": when data committed (flashback)• "Oracle Flashback" Tues. 11:20 (Connor McDonald)

– "Valid time":• start / end times determined by users• Past, present or future

• "Period": date/time range– Closed-Open: includes start time but not end time– Constraint: start time < end time– NULL start time = valid any time before end– NULL end time = valid from start time on

Page 8: Oracle 12c SQL: Date Ranges

8

What Oracle can do for you

create table valid_emp( ename varchar2(64), PERIOD FOR PRESENCE);

Page 9: Oracle 12c SQL: Date Ranges

9

select column_name, data_type, nullable,hidden_column, virtual_columnfrom user_tab_cols where table_name = 'VALID_EMP'order by internal_column_id;

Name Data Type Nullable Hidden VirtualPRESENCE_START TIMESTAMP(6) WITH TIME ZONE Y YES NOPRESENCE_END TIMESTAMP(6) WITH TIME ZONE Y YES NO

PRESENCE NUMBER Y YES YESENAME VARCHAR2 Y NO NO

select search_condition from user_constraintswhere table_name = 'VALID_EMP';

SEARCH_CONDITIONPRESENCE_START < PRESENCE_END

Page 10: Oracle 12c SQL: Date Ranges

10

insert allinto valid_emp (ename, presence_start, presence_end) values('Stew', date '1998-03-01', date '2015-12-01')into valid_emp (ename, presence_start, presence_end) values('Stew', date '2016-01-01', date '2050-12-01')select null from dual;

select * from valid_emp;

ENAME

Stew

StewOops! Hidden columns…

Page 11: Oracle 12c SQL: Date Ranges

11

select ename, presence_start, presence_endfrom valid_emp;

ENAME PRESENCE_START PRESENCE_END

Stew 1998-03-01 2015-12-01

Stew 2016-01-01 2050-12-01

Page 12: Oracle 12c SQL: Date Ranges

12

select ename, presence_start, presence_endfrom valid_empas of period for presence systimestamp;

ENAME PRESENCE_START PRESENCE_END

select * from table(dbms_xplan.display_cursor(format=>'+PREDICATE'));

filter(((T.PRESENCE_START IS NULL OR SYS_EXTRACT_UTC(T.PRESENCE_START)<=SYS_EXTRACT_UTC(SYSTIMESTAMP(6))) AND(T.PRESENCE_END IS NULL OR SYS_EXTRACT_UTC(T.PRESENCE_END) > SYS_EXTRACT_UTC(SYSTIMESTAMP(6)))))

Page 13: Oracle 12c SQL: Date Ranges

13

select ename, presence_start, presence_endfrom valid_empas of period for presence to_timestamp_tz('2015-11-01');

exec DBMS_FLASHBACK_ARCHIVE.ENABLE_AT_VALID_TIME ('CURRENT');select ename, presence_start, presence_endfrom valid_emp;

ENAME PRESENCE_START PRESENCE_END

Stew 1998-03-01 2015-12-01

ENAME PRESENCE_START PRESENCE_END

Page 14: Oracle 12c SQL: Date Ranges

14

Valid Time Rangeselect ename, presence_start, presence_endfrom valid_empversions period for presence between to_timestamp_tz('2015-11-01') and to_timestamp_tz('2016-01-01');

ENAME PRESENCE_START PRESENCE_END

Stew 1998-03-01 2015-12-01

Stew 2016-01-01 2050-12-01

Page 15: Oracle 12c SQL: Date Ranges

15

• So what did Oracle do for us?– Notion of "period"– Automatic, hidden column definitions– New query syntax with automatic rewrite

• And what did Oracle not do for us?– Query performance?– Temporal constraints?– Query for gaps or overlaps?– Temporal DML?

• Now: what can we do with Oracle?

Page 16: Oracle 12c SQL: Date Ranges

16

What we need to do:

• Temporal constraints• Performance: indexing• Temporal queries• Temporal DML

Page 17: Oracle 12c SQL: Date Ranges

17

Temporal Constraints• No gaps, no overlaps

– Don't use ranges!– "effective date"

• "start" = EFF_DATE , "end" = lead(EFF_DATE) over (EFF_DATE)

• Temporal primary keys– Use ID + start_date (usually)– ORA-02329: …TIMESTAMP WITH TIME ZONE cannot be unique or primary key

• Temporal foreign keys– Child range must be within parent range– "On commit" materialized views (hard to scale)– Triggers (hard to write correctly)

Page 18: Oracle 12c SQL: Date Ranges

18

Temporal DDL & Indexingcreate table valid_emp( ename varchar2(64), presence_start date, presence_end date not null, CHECK(presence_start < presence_end), period for presence(presence_start, presence_end), CONSTRAINT valid_emp_pk primary key (ename, presence_start) USING INDEX ( CREATE INDEX valid_emp_pk ON valid_emp (ename, presence_start, presence_end) ));

Page 19: Oracle 12c SQL: Date Ranges

19

Temporal Queries• Use Analytic functions

or 12c MATCH_RECOGNIZE• Typical use cases– find gaps: "free time" in calendars– Merge ranges that "meet"– Merge ranges that "meet" or "overlap"– Join on intersecting date ranges

• For simplicity, I assume "not NULL"

Page 20: Oracle 12c SQL: Date Ranges

20

Find GapsSTART_DATE END_DATE ---------- ----------2007-01-12 2007-01-252007-01-20 2007-02-012007-02-05 2007-02-102007-02-05 2007-02-282007-02-10 2007-02-152007-03-01 2007-03-022007-03-03 2007-03-16

SELECT end_date start_gap, LEAD(start_date) OVER (ORDER BY start_date) end_gap FROM T;

START_GAP END_GAP ---------- ----------2007-01-25 2007-01-202007-02-01 2007-02-052007-02-10 2007-02-052007-02-28 2007-02-102007-02-15 2007-03-012007-03-02 2007-03-032007-03-16

Page 21: Oracle 12c SQL: Date Ranges

21

Find GapsSTART_DATE END_DATE ---------- ----------2007-01-12 2007-01-252007-01-20 2007-02-012007-02-05 2007-02-102007-02-05 2007-02-282007-02-10 2007-02-152007-03-01 2007-03-022007-03-03 2007-03-16

SELECT * FROM ( SELECT MAX(end_date) OVER (ORDER BY start_date) start_gap, LEAD(start_date) OVER (ORDER BY start_date) end_gap FROM T)WHERE start_gap < end_gap;

START_GAP END_GAP ---------- ----------2007-01-25 2007-01-202007-02-01 2007-02-052007-02-28 2007-02-052007-02-28 2007-02-102007-02-28 2007-03-012007-03-02 2007-03-032007-03-16

Page 22: Oracle 12c SQL: Date Ranges

22

PACK: merge ranges that meetID START END1 01-01 01-032 01-03 01-053 02-05 02-284 02-10 02-155 03-01 03-116 03-10 03-127 03-11 03-21

select * from tmatch_recognize( order by start_date, end_date measures first(id) "first", last(id) "last", first(start_date) "start", last(end_date) "end" pattern(A* B) define A as end_date = next(start_date));

first last start end ----- ---- ----- ----- 1 2 01-01 01-05 3 3 02-05 02-28 4 4 02-10 02-15 5 5 03-01 03-11 6 6 03-10 03-12 7 7 03-11 03-21

overlap

intervening

Page 23: Oracle 12c SQL: Date Ranges

23

Merge ranges that meet or overlapID START END1 01-01 01-032 01-03 01-053 02-05 02-284 02-10 02-155 03-01 03-116 03-10 03-127 03-11 03-21

select * from tmatch_recognize( order by start_date, end_date measures first(id) "first", last(id) "last", first(start_date) "start", MAX(end_date) "end" pattern(A* B) define A as MAX(end_date) >= next(start_date));

first last start end ----- ---- ----- ----- 1 2 01-01 01-05 3 4 02-05 02-28 5 7 03-01 03-21

gap

gap

Page 24: Oracle 12c SQL: Date Ranges

24

Join on intersecting ranges

• Prerequisite for temporal DML– Join input to table to find affected rows

• Use UNION ALL to gather input and affected rows• Use UNPIVOT and DISTINCT to get range boundaries• Use LEAD() to create base ranges• DO LEFT JOIN from base ranges to table + input.

1 3 5 7

2 4

St12345

StEnd1 22 33 44 5

St End OldNew

1 2 1-32 3 1-3

2-43 4 3-5

2-44 5 3-5

Page 25: Oracle 12c SQL: Date Ranges

25

Questions?

More details at:stewashton.wordpress.com