#!/usr/bin/ksh # set -xv # author : B. Polarski # program : smenu_logminer.ksh # date : 22 Decembre 2005 HOST=`hostname` HOST=`echo $HOST | awk '{ printf ("%-+15.15s",$1) }'` LEN=70 ROWNUM=40 typeset -u ftable typeset -u fuser # ------------------------------------------------------------------------------------- function get_current_redo { ret=`sqlplus -s "$CONNECT_STRING" < -i lgm -l -a -desc/asc -rn -nc lgm -t [-u ] -a -desc/asc -r lgm -a -w "" lgm -a -xid # list all sql for XID lgm -a -sid [-ser ] # list all sql for session lgm -tbs -l : List the contents of the selected archives, display with counts -s : Give an SCN and retrieve the SQL statement -nc : Don't list commits in list -ist : Include rows whose operation is start | -sys : Include rows generated by sys rows -t : Table name | -u : Segment owner -i : force instance number | -v : Verbose -rn : Show at least rows | -tbs : Relocate logminer to -desc : Order output descending | -asc : Order output ascending -la : replace -a by -la to mine CURRENT redo logs Example: -------- List SQL from an SCN : lgm -s [-a ] List all rows for a archive log : lgm -l -a -ist List all rows for an object in an archive log : lgm -o -a -u List SCN,time and SQL for table CUST in archive 1244 : lgm -t CUST -a 1244 List all rows given a where clause : lgm -a -w "where operation not in ('UPDATE','DELETE','INSERT')" Column you can include in the where clause are any columns of V\$LOGMNR_CONTENTS lgm -a 11833 -rn 4000 -w "where operation in ('DELETE','INSERT') and username like 'BPA%'" lgm -la -sid <137> to view all entries of session 137 into current redo log Logminer in table: ------------------ Create and add logfile to logminer : lgm -cr [-a -a ..... ]. Creates smenu_tt in tbs sysaux index on: scn, XID, SID SERIAL#, USERNAME, seg_name lgm -tt -u # List all statement for from table smenu_tt EOF exit } # ------------------------------------------------------------------------------------- function do_execute { LLEN=`expr 55 + $LEN` echo "MACHINE $HOST - ORACLE_SID : $ORACLE_SID " sqlplus -s "$CONNECT_STRING" < -a ...... to add one or many archives" exit fi for i in $ARCH_LIST do unset ret ret=`get_archive_name $ARCH_NUM` if [ -n "$ret" ];then LST_ARCH="$LST_ARCH execute dbms_logmnr.Add_logfile(options => dbms_logmnr.new, logfilename=>'$ret'); " fi done SQL=" $LST_ARCH execute DBMS_LOGMNR.START_LOGMNR(options => dbms_logmnr.dict_from_online_catalog); drop table smenu_tt; set timing on; create table smenu_tt tablespace sysaux as select THREAD# ,SCN, timestamp, SEG_OWNER, SEG_NAME, SESSION# , SERIAL# ,USERNAME, OPERATION, SQL_REDO, xid , XIDUSN,XIDSLT,XIDSQN from v\$logmnr_contents ; create index idx_smenu_tt_xid on smenu_tt(XID) tablespace sysaux; create index idx_smenu_tt_sid on smenu_tt(SESSION#,SERIAL#) tablespace sysaux; create index idx_smenu_tt_usr on smenu_tt(USERNAME) tablespace sysaux; create index idx_smenu_tt_seg on smenu_tt(SEG_NAME) tablespace sysaux; create index idx_smenu_tt_scn on smenu_tt(scn) tablespace sysaux;" echo "$SQL" # ................................................. # Set logminer tablespace # ................................................. elif [ "$CHOICE" = "TBS" ];then SQL="EXECUTE DBMS_LOGMNR_D.SET_TABLESPACE('$ftbs');" # ................................................. # List contents of an archive log # ................................................. elif [ "$CHOICE" = "LIST" ];then ORDER=${ORDER:-order by scn desc} # default order by last scn ret=`get_archive_name $ARCH_NUM` # for cygwin we need to get rid of M$ garbage, hence the tr. name=`echo $ret | tr -d '\n'| awk '{print $1}'` SQL="set lines 190 pages 66 col xid for a16 execute dbms_logmnr.Add_logfile(options => dbms_logmnr.new, logfilename=>'$name'); execute DBMS_LOGMNR.START_LOGMNR(options => dbms_logmnr.dict_from_online_catalog); select seg_owner,scn,xid ,XIDUSN||'.'||XIDSLT||'.'||XIDSQN xid , cpt, -- son, operation, fdate from ( select seg_owner,xid, XIDUSN,XIDSLT,XIDSQN,scn, count(scn) cpt, seg_owner||'.'||seg_name son, operation, to_char(timestamp,'YYYY-MM-DD HH24:MI:SS') fdate from V\$LOGMNR_CONTENTS where $SYS $START $EXCLUDE_COMMIT 1=1 group by seg_owner, xid, XIDUSN,XIDSLT,XIDSQN, scn, seg_owner||'.'||seg_name , operation , to_char(timestamp,'YYYY-MM-DD HH24:MI:SS') $ORDER $DESC ) where rownum<=$ROWNUM ; " # ................................................. # List content of v$logmnr_contents given a where clause # ................................................. elif [ "$CHOICE" = "W_CLAUSE" ];then LEN_TEXT=100 if [ -n "$SID" ] ;then W_CLAUSE=" where session# = '$SID' order by timestamp " LEN_TEXT=90 VAR_FIELD="SESSION#, SERIAL#," VAR_FIELD_DISP="to_char(session#)||chr(10)||to_char(serial#) fsid," if [ -n "$SERIAL" ];then W_CLAUSE=" where session# = '$SID' and serial# = '$SERIAL' order by timestamp " fi WHERE=$W_CLAUSE elif [ -n "$XID" ] ;then W_CLAUSE=" where xid = '$XID' order by timestamp " WHERE=$W_CLAUSE elif [ -z "$W_CLAUSE" ];then # just order it by time desc if nothing was suggested WHERE=" order by scn desc" else WHERE=$W_CLAUSE fi ret=`get_archive_name $ARCH_NUM` name=`echo $ret | tr -d '\n'| awk '{print $1}'` SQL="execute dbms_logmnr.Add_logfile(options => dbms_logmnr.new, logfilename=>'$name'); execute DBMS_LOGMNR.START_LOGMNR(options => dbms_logmnr.dict_from_online_catalog); set lines 190 pages 66 col sql_redo for a$LEN_TEXT col opeartion for a9 col fscn for a22 head 'Scn|Schema' col fsid for a5 head 'Sess#|ser#' justify c break on scn on fdate on fsid report select scn||chr(10)||username fscn, fdate||chr(10)|| 'type='||operation fdate, $VAR_FIELD_DISP sql_redo from ( select scn,username, operation, sql_redo, $VAR_FIELD to_char(timestamp,'YYYY-MM-DD HH24:MI:SS') fdate from V\$LOGMNR_CONTENTS $WHERE )where rownum<=$ROWNUM ; " # ................................................. # List content of v$logmnr_contents for a given table # ................................................. elif [ "$CHOICE" = "FTABLE" ];then if [ -n "$ftable" ];then if [ -n "$fuser" ];then if [ "$ftable" = '%' ];then WHERE=" seg_owner='$fuser' " else WHERE=" seg_owner='$fuser' and table_name = '$ftable'" fi else if [ "$ftable" = '%' ];then unset WHERE else WHERE=" table_name = '$ftable'" fi fi fi ret=`sqlplus -s "$CONNECT_STRING" <= first_change# and $SCN < next_change# ; EOF` echo "ret=$ret" # for cygwin we need to get rid of M$ garbage, hence the tr. name=`echo $ret | tr -d '\n'| awk '{print $1}'` echo "name=$name" SQL="execute dbms_logmnr.Add_logfile(options => dbms_logmnr.new, logfilename=>'$name'); execute DBMS_LOGMNR.START_LOGMNR(options => dbms_logmnr.dict_from_online_catalog ); set pagesize 100 SELECT sql_redo FROM V\$LOGMNR_CONTENTS where scn = $SCN ; " #+ DBMS_LOGMNR.CONTINUOUS_MINE); # ................................................. # stop logminer # ................................................. elif [ "$CHOICE" = "STOP" ];then SQL="execute DBMS_LOGMNR.END_LOGMNR( ) ;" fi if [ -n "$VERBOSE" ];then echo "$SQL" fi if [ "$EXECUTE" = "YES" ];then do_execute else echo "$SQL" echo "--> (Add '-x' to your command to execute it)" fi