Oracle's Flashback Query - TIMESTAMP or SCN?June 18, 2009 Oracles Flashback Query(SQL-driven) makes use of both TIMESTAMP and SCN--but which should you use? It just may be a matter of preference but requires some thoughtful considerations. In order to take advantage of Oracles flashback query feature, for SQL-driven flashback, the SELECT statement makes use of the AS OF or VERSIONS BETWEEN clause to retrieve data from the past from tables, views, or materialized views.
SELECT ... FROM ...
VERSIONS BETWEEN { SCN | TIMESTAMP } { expr | MINVALUE }
Both the VERSIONS BETWEEN and the AS OF clause requires the user to code in the SQL an SCN or TIMESTAMP for proper retrieval of past information. The question often becomes should I use an SCN or should I use a TIMESTAMP? The answer is not always clear but Ill investigate both here to help you grapple with your next decision. First, it makes complete sense to define what a TIMESTAMP and an SCN is: TIMESTAMP is an extension of the DATE datatype, which stores all the information that the DATE datatype stores (month, day, year, hour, minute, second) but also includes fractional seconds. Personally, I find this a bit confusing since date, by the strictest definition, is JUST a particular month, day, and year when an event happened. Regardless, since TIMESTAMP is the datatype required for a SQL-driven the flashback query, the following SQL conversions could come in handy: Just as there is a call to SYSDATE to get the current system date and time, there is a call to get the current system TIMESTAMP. SQL> SELECT SYSTIMESTAMP FROM dual; SYSTIMESTAMP ------------------------------------- 11-JUN-09 10.17.52.622699 AM -06:00 IF you're interested in the current session TIMSTAMP in relation to the session time zone (SESSIONTIMEZONE) use the CURRENT_TIMESTAMP function call. SQL> SELECT CURRENT_TIMESTAMP FROM dual; CURRENT_TIMESTAMP ------------------------------------------- 11-JUN-09 10.18.02.510724 AM -06:00 Moreover, the old standby is to convert character datatypes for a known timestamp to a TIMESTAMP datatype:
SQL> SELECT TO_TIMESTAMP ('20090611 11:05:00', 'YYYYMMDD HH24:MI:SS') FROM dual;
TO_TIMESTAMP('2009061111:05:00','YYYYMMDDHH24:MI:SS')
-----------------------------------------------------
11-JUN-09 11.05.00.000000000 AM
If you must use sysdate, which isn't the best option, you would have to do: SQL> SELECT TO_TIMESTAMP(TO_CHAR(sysdate,'YYYYMMDD HH24:MI:SS'),'YYYYMMDD HH24:MI:SS') FROM dual; TO_TIMESTAMP(TO_CHAR(SYSDATE,'YYYYMMDDHH24:MI:SS'),'YYYYMMDDHH24:MI:SS') --------------------------------------------------------------------------- 11-JUN-09 10.01.18.000000000 AM The use of TIMESTAMPs become very important if you know a particular point in time or want to look for something within a time period as shown in the following two SQL statements. To look at what a specific value was at a point in time; say 10 minutes ago:
SELECT *
FROM flash_test
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '10' MINUTE);
To look at what values were during a particular time period; say between 10 minutes ago and now:
SELECT *
FROM flash_test
VERSIONS BETWEEN TIMESTAMP
SYSTIMESTAMP - INTERVAL '10' MINUTE AND SYSTIMESTAMP;
SCN (System Change Number) is nothing more than a stamp (number) that defines a committed version of a database at a point in time. This means that every committed transaction is assigned a unique SCN. Worthy to note, Oracle will also perform internal work that generates SCNs. I think TIMESTAMP has probably overtaken the use of SCN mostly because many just don't know where to extract a valid SCN from. Here are a few areas that could help you in this endeavor. Current SCN of the database.
SQL> SELECT DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER() FROM DUAL;
DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER()
-----------------------------------------
2249659
SQL> SELECT CURRENT_SCN FROM V$DATABASE;
CURRENT_SCN
-----------
2249662
Just remember that there are literally 100's of tables and views within Oracle that maintain some form of an SCN. Use the following SQL statement to find which ones you might want to use for your next flashback query. SELECT owner, table_name, column_name FROM dba_tab_columns WHERE column_name LIKE '%SCN%' OWNER TABLE_NAME COLUMN_NAME ----- ------------------------------ ------------------------------ SYS V_$DATABASE CURRENT_SCN SYS V_$DATABASE STANDBY_BECAME_PRIMARY_SCN SYS V_$DATAFILE FIRST_NONLOGGED_SCN SYS V_$FILESPACE_USAGE CHANGESCN_BASE SYS V_$FILESPACE_USAGE CHANGESCN_WRAP SYS V_$FLASHBACK_DATABASE_LOG OLDEST_FLASHBACK_SCN SYS V_$KCCDI DICKP_SCN SYS V_$KCCDI DICUR_SCN SYS V_$KCCDI DISSC_SCN SYS V_$KCCDI DISCN SYS V_$KCCFE FECRC_SCN SYS V_$KCCFE FEONC_SCN SYS V_$KCCFE FEPLG_SCN SYS V_$LOGMNR_CONTENTS CSCN SYS V_$LOGMNR_CONTENTS SAFE_RESUME_SCN SYS V_$LOGMNR_CONTENTS SCN SYS V_$LOGMNR_CONTENTS START_SCN SYS V_$LOGMNR_CONTENTS COMMIT_SCN SYS V_$LOGMNR_DICTIONARY RESET_SCN_TIME SYS V_$LOGMNR_DICTIONARY DB_TXN_SCN SYS V_$LOGMNR_DICTIONARY RESET_SCN SYS V_$LOGMNR_DICTIONARY DICTIONARY_SCN SYS V_$LOGMNR_LOGFILE LOW_SCNBAS SYS V_$LOGMNR_LOGFILE RESET_SCNWRP SYS V_$LOGMNR_LOGFILE RESET_SCNBAS SYS V_$LOGMNR_LOGFILE RESET_SCN_TIME SYS V_$LOGMNR_LOGFILE NEXT_SCNWRP SYS V_$LOGMNR_LOGFILE NEXT_SCNBAS SYS V_$LOGMNR_LOGFILE LOW_SCNWRP SYS V_$LOGMNR_LOGS NEXT_SCN SYS V_$LOGMNR_LOGS LOW_SCN SYS V_$LOGMNR_LOGS RESET_SCN_TIME SYS V_$LOGMNR_LOGS RESET_SCN SYS V_$LOGMNR_PARAMETERS START_SCN SYS V_$LOGMNR_PARAMETERS END_SCN SYS V_$LOGMNR_PARAMETERS REQUIRED_START_SCN SYS V_$LOGMNR_SESSION END_SCN SYS V_$LOGMNR_SESSION READ_SCN SYS V_$LOGMNR_SESSION RESET_SCN SYS V_$LOGMNR_SESSION LOW_MARK_SCN SYS V_$LOGMNR_SESSION CONSUMED_SCN SYS V_$LOGMNR_SESSION PROCESSED_SCN SYS V_$LOGMNR_SESSION START_SCN SYS V_$LOGMNR_SESSION SPILL_SCN SYS V_$LOGMNR_SESSION PREPARED_SCN ... And MANY MORE.. Use of an SCN in SQL-driven flashback queries isnt much different than using a TIMESTAMP. Just plug in your SCN number.
SELECT *
FROM flash_test
AS OF SCN 2249100;
SELECT *
FROM flash_test
VERSIONS BETWEEN SCN 2249600 AND 2249659;
You can also play around with converting from a TIMESTAMP to an SCN and an SCN to a TIMESTAMP. These come in handy if you want to standardize on a particular method or help you in pinpointing which is better to use during recovery scenarios. Convert SCN to TIMESTAMP.
SQL> SELECT SCN_TO_TIMESTAMP(2249659) FROM dual;
SCN_TO_TIMESTAMP(2249659)
------------------------------------------------
11-JUN-09 10.30.09.000000000 AM
Convert TIMESTAMP to SCN
SQL> SELECT TIMESTAMP_TO_SCN('11-JUN-09 10.30.09.000000000 AM') FROM dual;
TIMESTAMP_TO_SCN('11-JUN-0910.30.09.000000000AM')
-------------------------------------------------
2249659
Probably worth mentioning, since flashback query isn't "supposed" to work across database bounces, is the following query to extract the initial TIMESTAMP and SCN from instance startup. SQL> SELECT TO_TIMESTAMP(TO_CHAR(startup_time,'YYYYMMDDHH24MISS'),'YYYYMMDDHH24MISS') TIMESTAMP, 2 TIMESTAMP_TO_SCN(TO_TIMESTAMP(TO_CHAR(startup_time,'YYYYMMDDHH24MISS'),'YYYYMMDDHH24MISS') ) SCN 3 FROM v$instance; TIMESTAMP SCN ----------------------------------- ---------- 11-JUN-09 06.49.04.000000000 AM 2237632 Oftentimes, but not always, flashback queries that attempt to SELECT data before these times will generate the following error:
SQL> SELECT *
2 FROM flash_test
3 AS OF SCN 1000000;
FROM flash_test
*
ERROR at line 2:
ORA-01466: unable to read data - table definition has changed
Determining to use an SCN or TIMESTAMP for flashback queries seems to be a matter of comfort. I think it really comes down to whether youre comfortable working with times or numbers. Worthy to note, since there are various human factors that come into play when discussing time (TIMEZONE and client-side wall clock or system time to name just a couple) it may be better to always convert to a real database server side TIMESTAMP and then an SCN. Moreover, this sounds much more professional, can help obfuscate the issue, and make you sound like youre remaining technical; always a plus! |