Pages

Wednesday, August 22, 2018

bash and sqlplus by Example: Kill Long Running Jobs

===============================
General
===============================
Known jobs should be running for only few minutes.
However, since they are connected via DB_LINKS to several remote databases, due to networks issues connection can be stuck, and job is hanged.
The workaround - it to kill this job, and start execution again.

Following code is executed from crontab every 15 minutes, and checks for jobs which run longer than 30 minutes.

===============================
Code
===============================

less kill_long_running_jobs.sh

#!/bin/bash
. /etc/profile
. /etc/sh/orash/oracle_login.sh igt

handle_return_code() {
  ret_code=$1
  step_name=$2
  if [[ $ret_code -ne 0 ]];then
    echo $step_name Finished With Error!! Return Code $ret_code
    exit $ret_code
  fi
}

WORK_DIR=/software/oracle/oracle/scripts
LOG_DIR=${WORK_DIR}
RUN_TIME=`date +"%Y%m%d_%H%M%S"`
LOG_FILE=killed_sessions.log
TEMP_FILE=long_running_sessions.tmp

cd ${WORK_DIR}
touch ${LOG_DIR}/${LOG_FILE}
rm -f ${TEMP_FILE}

sqlplus -s /nolog <<EOF
whenever sqlerror exit SQL.SQLCODE
@./kill_long_running_jobs.sql
EOF
handle_return_code $? ./kill_long_running_jobs.sql

if [[ ! -f ${TEMP_FILE} || ! -s ${TEMP_FILE} ]]; then
  echo "$RUN_TIME   No Long Running Sessions Found" >> ${LOG_DIR}/${LOG_FILE}
  exit 0
fi

echo reading file ${TEMP_FILE}

while read line
do
  KILL_CMD=`echo $line | awk 'BEGIN {FS="~"}{print $1}'`
  WHAT=`echo $line | awk 'BEGIN {FS="~"}{print $2}'`

  echo "$RUN_TIME   ${KILL_CMD} ${WHAT}" >> ${LOG_DIR}/${LOG_FILE}
  ${KILL_CMD}

done < ${TEMP_FILE}

less kill_long_running_jobs.sql

@./set_user.sql
connect &&user/&&pass@&&conn_str
PROMPT CONNECTED TO &&user/&&pass@&&conn_str

SET HEADING OFF
SET PAGESIZE 0
SET LINESIZE 200
SET NEWPAGE NONE
SET FEEDBACK OFF
SET VERIFY OFF
SPOOL long_running_sessions.tmp

SELECT 'kill -9 '||V_PROCESS.spid||'~'||DBA_JOBS.what AS LINUX_KILL
FROM   V$SESSION V_SESSION,
       V$PROCESS V_PROCESS,
       DBA_JOBS_RUNNING STUCK_JOBS,
       DBA_JOBS
WHERE  V_PROCESS.addr = V_SESSION.paddr
  AND  V_SESSION.type != 'BACKGROUND'
  AND  V_SESSION.sid = STUCK_JOBS.sid
  AND  DBA_JOBS.job = STUCK_JOBS.job
  AND  logon_time < (SYSDATE - 30/1440)
  AND  UPPER(DBA_JOBS.what) IN ('EXT_MOCO.SCHEDULE;','EXT_OVMD.SCHEDULE;','EXT_IPN.SCHEDULE;','EXT_GLR.SCHEDULE;','EXT_SPARX.SCH
EDULE;')
UNION ALL
SELECT 'kill -9 4545~EXT_SPARX.SCHEDULE;' AS LINUX_KILL
FROM DUAL
WHERE 1=2;

SPOOL OFF

less set_user.sql

DEFINE user=user_name
DEFINE pass=user_pass
DEFINE conn_str=orainst

Code Example. Keep History of a Table Split into Several Tables

===============================
General
===============================
Purpose: Keep history of a fast growing table with performing DELETE.
This is a LOG table, so not important to keep all records, thus ROWNUM < 10001 limit was used per day.
Under normal execution - the number of entries in LOG tables is small.
It is when system encounter some unexpected behavior, is when the LOG table is overloaded with error messages having same text over and over again.


===============================
Code
===============================

PL/SQL Code
CREATE OR REPLACE PACKAGE BODY ADMIN_UTIL IS

PROCEDURE move_tb_2_tb  (p_source_table_name IN VARCHAR2,
                         p_target_table_name IN VARCHAR2) IS

  v_module_name  VARCHAR2(100);
  v_sql_str      VARCHAR2(1000);
  v_msg_str      VARCHAR2(1000);
  
BEGIN

  v_module_name := 'ADMIN_UTIL.move_tb_2_tb';
  
  v_sql_str := 'TRUNCATE TABLE '||p_target_table_name;
  EXECUTE IMMEDIATE v_sql_str;

  v_sql_str :=   'INSERT /*+ APPEND */ INTO '||p_target_table_name||' SELECT * FROM '||p_source_table_name||' WHERE rownum < 10001';
  EXECUTE IMMEDIATE v_sql_str;
  COMMIT;

  EXCEPTION
    WHEN OTHERS THEN
      v_msg_str := 'Unexpected Error: '||SQLERRM;
      SGA_PKG.write_sga_w_log(v_module_name, v_msg_str);
END move_tb_2_tb;

PROCEDURE purge_sga_w_log IS

  v_module_name  VARCHAR2(100);
  v_msg_str      VARCHAR2(1000);  
  v_origin_table VARCHAR2(30);
  v_source_table VARCHAR2(30);
  v_target_table VARCHAR2(30);  
  v_index        NUMBER;
  v_max_index    NUMBER;
BEGIN

  v_module_name := 'ADMIN_UTIL.purge_sga_w_log';
  v_origin_table := 'SGA_W_LOG';

  v_msg_str := 'Starting';
  SGA_PKG.write_sga_w_log(v_module_name, v_msg_str);
  v_max_index := 7;
  v_index := v_max_index;

  
  WHILE v_index > 0 LOOP
    IF v_index = 1 THEN
      v_source_table := v_origin_table;      
    ELSE            
      v_source_table := v_origin_table||'_'||TO_CHAR(v_index-1);
    END IF;  
    v_target_table := v_origin_table||'_'||TO_CHAR(v_index);  
    move_tb_2_tb(v_source_table, v_target_table);
    v_index := v_index -1;
  END LOOP;  
  
  EXECUTE IMMEDIATE 'TRUNCATE TABLE '||v_origin_table;

  v_msg_str := 'Finished';
  SGA_PKG.write_sga_w_log(v_module_name, v_msg_str);
  
  EXCEPTION
     WHEN OTHERS THEN
        v_msg_str := 'Unexpected error: '||SUBSTR(SQLERRM, 1, 900);
        BEGIN
          SGA_PKG.write_sga_w_log(v_module_name, v_msg_str);
        EXCEPTION
          WHEN OTHERS THEN
            NULL;
        END;      
  END purge_sga_w_log;        
END  ADMIN_UTIL;


View on top of the partial tables.
CREATE OR REPLACE VIEW SGA_W_LOG_VW
AS SELECT log_table, PROCEDURE_NAME, data, TO_CHAR(ts_last_modified,'YYYYMMDD hh24:mi:ss') AS ts_last_modified FROM (
SELECT 'SGA_W_LOG' AS log_table, procedure_name, data, ts_last_modified FROM SGA_W_LOG  
UNION ALL
SELECT 'SGA_W_LOG_1' AS log_table, procedure_name, data, ts_last_modified FROM SGA_W_LOG_1
UNION ALL
SELECT 'SGA_W_LOG_2' AS log_table, procedure_name, data, ts_last_modified FROM SGA_W_LOG_2
UNION ALL
SELECT 'SGA_W_LOG_3' AS log_table, procedure_name, data, ts_last_modified FROM SGA_W_LOG_3
UNION ALL
SELECT 'SGA_W_LOG_4' AS log_table, procedure_name, data, ts_last_modified FROM SGA_W_LOG_4
UNION ALL
SELECT 'SGA_W_LOG_5' AS log_table, procedure_name, data, ts_last_modified FROM SGA_W_LOG_5
UNION ALL
SELECT 'SGA_W_LOG_6' AS log_table, procedure_name, data, ts_last_modified FROM SGA_W_LOG_6
UNION ALL
SELECT 'SGA_W_LOG_7' AS log_table, procedure_name, data, ts_last_modified FROM SGA_W_LOG_7
)
ORDER BY ts_last_modified DESC

Tuesday, August 21, 2018

Procedure to Sync values in Tables with values in Sequences.

=====================================
General
=====================================
Procedure to Sync values in Tables with values in Sequences.
If table value higher then sequence value, the value in sequence is advanced.

=====================================
Code
=====================================

PROCEDURE fix_sequences (p_table_name    IN VARCHAR2, 
                         p_sequence_name IN VARCHAR2) IS

  v_table_max       NUMBER;
  v_sequence_val    NUMBER;
  v_dummy           NUMBER;
  v_exists          NUMBER;
  v_loop            NUMBER;
  v_count_fields_pk NUMBER;
  v_field_name_pk   USER_CONS_COLUMNS.column_name%TYPE;
  v_sql_str         VARCHAR2(4000);

BEGIN

  --Check If Requested Table exists
  SELECT COUNT(*) INTO v_exists
  FROM USER_TABLES
  WHERE table_name = UPPER(p_table_name);
  IF v_exists = 0 Then
     RAISE_APPLICATION_ERROR(-20000,'Requested Table <' || p_table_name || '> does not exists ' || SQLERRM);
  END IF;

   --Check If Requested Sequence exists
  SELECT COUNT(*) INTO v_exists
  FROM USER_SEQUENCES
  WHERE sequence_name = UPPER(p_sequence_name);
  IF v_exists = 0 Then
    RAISE_APPLICATION_ERROR(-20000,'Requested Sequence <' || p_sequence_name || '> does not exists ' || SQLERRM);
  END IF;


  -- Find Field Name of PK for Sequence ...
  SELECT COUNT(*) INTO v_count_fields_pk
  FROM USER_CONSTRAINTS c, 
       USER_CONS_COLUMNS cc
  WHERE c.constraint_type = 'P'
    AND c.constraint_name = cc.constraint_name
    AND c.table_name = UPPER(p_table_name);

   IF v_count_fields_pk = 0 THEN
     RAISE_APPLICATION_ERROR(-20000,'No PK for Requested table <' || p_table_name || '> ' || SQLERRM);
   ELSIF v_count_fields_pk > 1 THEN
     RAISE_APPLICATION_ERROR(-20000,'PK of Requested table <' || p_table_name || '> has more then 1 field ' || SQLERRM);
   ELSE
     NULL; --No problem, Found the correct PK
   END IF;

   BEGIN
     SELECT cc.column_name INTO v_field_name_pk
       FROM USER_CONSTRAINTS C, 
            USER_CONS_COLUMNS CC
   WHERE c.constraint_type = 'P'
     AND c.constraint_name = cc.constraint_name
     AND c.table_name = UPPER(p_table_name);
   EXCEPTION
     WHEN OTHERS THEN
     RAISE_APPLICATION_ERROR(-20000,'Error while finding PK field name for table <' || p_table_name || '> ' || SQLERRM);
   END;

   --Find MaxValue in Table
   v_sql_str := 'SELECT NVL(MAX('||v_field_name_pk||'),0) FROM '||p_table_name;
   EXECUTE IMMEDIATE v_sql_str INTO v_table_max;

   --Find NextVal of Sequence
   v_sql_str := 'SELECT '||p_sequence_name||'.NEXTVAL FROM DUAL';
   EXECUTE IMMEDIATE v_sql_str INTO v_sequence_val;

   --Compare 2 Values AND Move Sequence if Needed
   IF v_table_max > v_sequence_val THEN 
     v_loop := v_table_max - v_sequence_val;
     FOR i IN 0..v_loop LOOP
       v_sql_str := 'SELECT ' || p_sequence_name || '.NEXTVAL FROM DUAL';
       EXECUTE IMMEDIATE v_sql_str INTO v_dummy;
     END LOOP;
   END IF;

END fix_sequences;

Wednesday, August 15, 2018

Code Example: PL/SQL code that parse a string with delimiter into a table, and epoch time date conversion

======================
General
======================
Code Example: PL/SQL code that parse a string with delimiter into a table, and epoch time date conversion.
The results are stored in a table.

======================
Files
======================
PARSE_CAMPAIGN_PKG.sql
CAMPAIGN_SUBSCRIBERS.sql

======================
Code
======================

CAMPAIGN_SUBSCRIBERS.sql
CREATE TABLE CAMPAIGN_SUBSCRIBERS
(
  campaign_id                   NUMBER(22),
  imsi                          VARCHAR2(30),
  msisdn                        VARCHAR2(30),
  campaign_date                 DATE
) TABLESPACE IGT_TABLE; 


PARSE_CAMPAIGN_PKG.sql
---------------------------------
-- A code that parse campaign_history with id 102 from format
--1534249100:102:20624094159360-20624094159361-20624094159362:89:1;
--1533638521:102:20624089860956-20624089860958-20624089860959:89:1;
--1533586549:102:20624089281354-20624089281363-20624089281364:218:1;
--etc...
--epoc time:102:other_info:xxx:1;
-- To a table. 
---------------------------------

CREATE OR REPLACE PACKAGE PARSE_CAMPAIGN_PKG IS
  PROCEDURE parse_by_campaign_id (p_capmaign_id       IN CAMPAIGN_SUBSCRIBERS.campaign_id%TYPE,
                                  p_start_date_epoch  IN VARCHAR2,
                                  p_end_date_epoch    IN VARCHAR2);
END PARSE_CAMPAIGN_PKG;
/

CREATE OR REPLACE PACKAGE BODY PARSE_CAMPAIGN_PKG IS
----------------------------------------------
  PROCEDURE write_log(p_module_name SGA_W_LOG.procedure_name%TYPE,
                      p_text        SGA_W_LOG.data%TYPE) IS
  PRAGMA AUTONOMOUS_TRANSACTION;

  BEGIN
    INSERT INTO SGA_W_LOG  (procedure_name, data, ts_last_modified)
    VALUES  (p_module_name, p_text, sysdate);
    COMMIT;

  EXCEPTION
    WHEN OTHERS THEN
      NULL;
  END write_log;
  
  
PROCEDURE cre_entry_campaign_sub (p_subscriber_rec     IN SGA_W_PSMS_SUBSCRIBER%ROWTYPE,
                                  p_campaign_id        IN CAMPAIGN_SUBSCRIBERS.campaign_id%TYPE ,
                                  p_campaign_date      IN CAMPAIGN_SUBSCRIBERS.campaign_date%TYPE) IS
                                  
  v_module_name        SGA_W_LOG.procedure_name%TYPE;
  v_msg_text           SGA_W_LOG.data%TYPE;  
  
BEGIN
  v_module_name := 'cre_entry_campaign_sub';
  INSERT INTO CAMPAIGN_SUBSCRIBERS(campaign_id, imsi, msisdn, campaign_date ) 
  VALUES (p_campaign_id, p_subscriber_rec.imsi, p_subscriber_rec.msisdn, p_campaign_date);
  COMMIT;
                           
EXCEPTION
  WHEN OTHERS THEN
    v_msg_text := 'Unexpected Error: '||SQLERRM;
    write_log(v_module_name, v_msg_text);
    RAISE;    
END cre_entry_campaign_sub;  
----------------------------------------------
-- 1531864267:99:17535087551370-17535087551371-17535087551372-17535087551373:1162:1;
---------------------------------------------- 
PROCEDURE handle_campaign (p_subscriber_rec     IN SGA_W_PSMS_SUBSCRIBER%ROWTYPE,
                           p_campaign_text      IN VARCHAR2,
                           p_campaign_id        IN CAMPAIGN_SUBSCRIBERS.campaign_id%TYPE ,
                           p_start_date_epoch  IN VARCHAR2,
                           p_end_date_epoch    IN VARCHAR2) IS
                           
                             
  v_module_name        SGA_W_LOG.procedure_name%TYPE;
  v_msg_text           SGA_W_LOG.data%TYPE;  
  
  v_epoch_date         VARCHAR2(100);
  v_text_delimiter     VARCHAR2(1);  
  v_campaign_date      DATE;
  
BEGIN
  v_module_name := 'handle_campaign';
  v_text_delimiter := ':';
  
  v_epoch_date := SUBSTR(p_campaign_text,1, INSTR(p_campaign_text,v_text_delimiter)-1);
    
  IF (p_start_date_epoch <= v_epoch_date) AND (v_epoch_date <= p_end_date_epoch) THEN
    --24*60*60*1000 = 86400
    v_campaign_date := TO_DATE('19700101','YYYYMMDD') + (1/86400 * v_epoch_date);
  
    cre_entry_campaign_sub(p_subscriber_rec, p_campaign_id, v_campaign_date); 
  END IF;
     
EXCEPTION
  WHEN OTHERS THEN
    v_msg_text := 'Unexpected Error: '||SQLERRM;
    write_log(v_module_name, v_msg_text);
    RAISE;    
END handle_campaign;
----------------------------------------------
PROCEDURE handle_subscriber (p_subscriber_rec     IN SGA_W_PSMS_SUBSCRIBER%ROWTYPE,
                             p_campaign_id        IN CAMPAIGN_SUBSCRIBERS.campaign_id%TYPE,
                             p_start_date_epoch  IN VARCHAR2,
                             p_end_date_epoch    IN VARCHAR2) IS
                             
  v_module_name        SGA_W_LOG.procedure_name%TYPE;
  v_msg_text           SGA_W_LOG.data%TYPE;
  
  v_campaigns_list      SGA_W_PSMS_SUBSCRIBER.campaign_history%TYPE; 
  v_curr_campaign       SGA_W_PSMS_SUBSCRIBER.campaign_history%TYPE;  
  
  v_search_campaign     VARCHAR2(100);  
  v_campaign_delimiter  VARCHAR2(1);  
  v_next_campaign_index NUMBER;  
  
BEGIN
  
  v_module_name := 'handle_subscriber';
  v_campaign_delimiter := ';';
  v_next_campaign_index := 1;
  v_campaigns_list := p_subscriber_rec.campaign_history;
  v_search_campaign := ':'||TO_CHAR(p_campaign_id)||':';
--  v_campaigns_list_curr := v_campaigns_list;
  
  WHILE v_next_campaign_index > 0 LOOP
    v_next_campaign_index := INSTR(v_campaigns_list,v_campaign_delimiter);
    v_curr_campaign := SUBSTR(v_campaigns_list,1,v_next_campaign_index);
    v_campaigns_list := SUBSTR(v_campaigns_list,v_next_campaign_index+1);
    
    IF INSTR(v_curr_campaign,v_search_campaign) > 0 THEN
      handle_campaign(p_subscriber_rec, v_curr_campaign, p_campaign_id, p_start_date_epoch,  p_end_date_epoch);
    END IF;  
    
    IF v_campaigns_list = ';' THEN
      v_next_campaign_index := 0;
    END IF;
    
  END LOOP;  

EXCEPTION
  WHEN OTHERS THEN
    v_msg_text := 'Unexpected Error: '||SQLERRM;
    write_log(v_module_name, v_msg_text);
    RAISE;    
END handle_subscriber;

----------------------------------------------
PROCEDURE parse_by_campaign_id (p_capmaign_id       IN CAMPAIGN_SUBSCRIBERS.campaign_id%TYPE,
                                p_start_date_epoch  IN VARCHAR2,
                                p_end_date_epoch    IN VARCHAR2) IS

  v_module_name         SGA_W_LOG.procedure_name%TYPE;
  v_msg_text            SGA_W_LOG.data%TYPE;
  v_seach_campaign_str  VARCHAR2(100);

  CURSOR get_campaign_subs_cur (cp_capmaign_id IN VARCHAR) IS
  SELECT *
    FROM SGA_W_PSMS_SUBSCRIBER
   WHERE INSTR(campaign_history,cp_capmaign_id) > 0;

 BEGIN
  v_module_name := 'parse_by_campaign_id';
  v_msg_text := 'Starting';
  write_log(v_module_name, v_msg_text);

  v_seach_campaign_str := ':'||p_capmaign_id||':';

  FOR get_campaign_subs_rec IN get_campaign_subs_cur(TO_CHAR(v_seach_campaign_str)) LOOP
    handle_subscriber(get_campaign_subs_rec, p_capmaign_id, p_start_date_epoch, p_end_date_epoch);    
  END LOOP;

  v_msg_text := 'Finished';
  write_log(v_module_name, v_msg_text);

EXCEPTION
  WHEN OTHERS THEN
  v_msg_text := 'Unexpected Error: '||SQLERRM;
  write_log(v_module_name, v_msg_text);
  RAISE;    
END parse_by_campaign_id;

----------------------------------------------
END PARSE_CAMPAIGN_PKG ;
/

Sunday, August 12, 2018

ORA 1578 - Block Corruption causing multiple Error Messages

============================
General
============================
Huge trace files re generated from archiver process.
When checking alert.log, there are these messages:
ORA-01578; ORACLE data block corrupted


Other then that, no other error messages are coming.


How to fix this issue?

============================
Errors
============================
Errors in alert.log

Thu Aug 09 10:29:26 2018
DDE: Problem Key 'ORA 1578' was completely flood controlled (0x6)
Further messages for this problem key will be suppressed for up to 10 minutes
Thu Aug 09 10:31:07 2018
Thread 1 advanced to log sequence 200953 (LGWR switch)
  Current log# 1 seq# 200953 mem# 0: /oracle_db/db1/db_igt/ora_redo_01_a.rdo
Thu Aug 09 10:33:48 2018
Thread 1 advanced to log sequence 200954 (LGWR switch)
  Current log# 2 seq# 200954 mem# 0: /oracle_db/db1/db_igt/ora_redo_02_a.rdo
Thu Aug 09 10:36:29 2018
Thread 1 advanced to log sequence 200955 (LGWR switch)
  Current log# 3 seq# 200955 mem# 0: /oracle_db/db1/db_igt/ora_redo_03_a.rdo
Thu Aug 09 10:39:10 2018
Thread 1 advanced to log sequence 200956 (LGWR switch)
  Current log# 1 seq# 200956 mem# 0: /oracle_db/db1/db_igt/ora_redo_01_a.rdo
Thu Aug 09 10:39:26 2018
DDE: Problem Key 'ORA 1578' was completely flood controlled (0x6)
Further messages for this problem key will be suppressed for up to 10 minutes
Thu Aug 09 10:41:53 2018
Thread 1 advanced to log sequence 200957 (LGWR switch)
  Current log# 2 seq# 200957 mem# 0: /oracle_db/db1/db_igt/ora_redo_02_a.rdo
Thu Aug 09 10:44:33 2018
Thread 1 advanced to log sequence 200958 (LGWR switch)
  Current log# 3 seq# 200958 mem# 0: /oracle_db/db1/db_igt/ora_redo_03_a.rdo
Thu Aug 09 10:47:10 2018
Thread 1 advanced to log sequence 200959 (LGWR switch)
  Current log# 1 seq# 200959 mem# 0: /oracle_db/db1/db_igt/ora_redo_01_a.rdo
Thu Aug 09 10:49:27 2018
DDE: Problem Key 'ORA 1578' was completely flood controlled (0x6)
Further messages for this problem key will be suppressed for up to 10 minutes


============================
General
============================

Definitions.
PHYSICAL corruption -  a Block could not be written
Logical corruption -  a Block could be written, but cannot be updated
Soft Corrupt - A Logical Corrupt block may be marked as Logical corruption

A. Soft Corrupt Blocks - Definition and information (Doc ID 1496934.1)

PURPOSE
Oracle may mark a block as Soft Corrupt.  
This document explains the definition of Soft Corrupt Block and the situations and behaviour when a block is marked as Soft Corrupt. 

DETAILS

What is a Soft Corrupt Block?
A Software Corrupt Block is:

1) Former LOGICAL corrupt block marked as formally corrupt or


2) A block that was attempted to be recovered in the buffer cache and could not be recovered. 

   The next message is indication of marking a block as soft corrupt in memory because the automatic block recover could not recover the block:

When a block is marked as Soft Corrupt?

When db_block_checking is enabled, Oracle soft corrupts an already corrupt block when modifying it (the block before image is already corrupt meaning that the block may be already corrupt on DISK).  
This only applies for LOGICAL corruptions as with PHYSICAL corruptions the block is detected as corrupt during read and does not arrive to the checking code where a modification takes place.
When automatic block recover could not recover the block after a process failed during block modification.  "Block recovery logically corrupted file .. block .." message appears in the alert.log
dbms_repair.FIX_CORRUPT_BLOCKS can also be used to mark a LOGICAL corrupt block as Soft Corrupt.
Media recovery can also soft corrupt a block if the block is already PHYSICAL corrupt
Subsequent block reads then produce ORA-1578 instead of internal errors.

Why is a block marked as Soft Corrupt?

A block is marked as soft corrupt to prevent other internal errors and to prevent further corruptions in the database.  
When a block is logically corrupt it may cause ORA-600 / ORA-7445 errors and may produce further corruptions for other blocks in the Oracle buffer cache memory when the corrupt block is modified.  Marking the block as soft corrupt prevents DML SQL statements to modify the block.  When a block is marked as soft corrupt, SQL statements fail with error ORA-1578 when the block is read.
A block is also marked as Soft Corrupt so it can be skipped when using event 10231 or when using procedure dbms_repair.SKIP_CORRUPT_BLOCKS.
When using TDE (Transparent Data Encryption - Tablespace encryption), a block can be marked as soft corrupt if an incorrect or invalid wallet is used.  Note that for this case the block is only soft corrupt in Memory as long as the invalid wallet is used (dbverify does not produce an error as the block is not soft corrupt on disk).


What is the Oracle behavior for a Soft Corrupt Block?

When a block is marked as soft corrupt, SQL statements fail with error ORA-1578 when reading the block unless dbms_repair.SKIP_CORRUPT_BLOCKS or event 10231/10233 are used.
RMAN backups do not fail with Soft Corrupt blocks.  RMAN ignores 'soft corrupt' blocks during backup without setting the MAXCORRUPT clause.  Soft Corrupt blocks do not count in the MAXCORRUPT clause.
Media Recovery (rollforward) ignores Soft Corrupt blocks.  In general media RECOVERY ignores Physical corrupt blocks and Soft Corrupt corrupt blocks.  When the block is logically corrupt Media RECOVERY may fail with unexpected errors. When the block is PHYSICAL corrupt, media recovery marks the block as soft corrupt and recovery continues with no errors (Corrupt block is skipped).
RMAN validate does not report a soft corrupt block in the trace file but in v$database_block_corruption. Example:
SQL> select * from v$database_block_corruption;

     FILE#     BLOCK#     BLOCKS CORRUPTION_CHANGE# CORRUPTIO

---------- ---------- ---------- ------------------ ---------
         1      59744          1                  0 CORRUPT
DBVerify reports the soft corrupt block with DBV-200 error:

DBV-00200: Block, dba <rdba>, already marked corrupted


============================
Options to identify the Corruption
============================
Option A - using IPS
Using IPS it is possible to use a CLI commands, that would identify the corrupted blocks, general a zip file with all related information, and then use oracle tool to read the generated zip.

>adrci

ADRCI: Release 11.1.0.7.0 - Production on Sun Aug 12 06:57:59 2018

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

ADR base = "/software/oracle"

adrci>  SHOW PROBLEM

ADR Home = /software/oracle/diag/rdbms/igt/igt:
*************************************************************************
PROBLEM_ID           PROBLEM_KEY                                                 LAST_INCIDENT        LASTINC_TIME
-------------------- ----------------------------------------------------------- -------------------- ----------------------------------------
3                    ORA 1578                                                    915474               2018-08-01 12:27:46.962289 +00:00
2                    ORA 600 [ORA-00600: internal error code, arguments: [3020], 715347               2018-06-17 14:55:00.738747 +00:00
1                    ORA 600 [3020]                                              715379               2018-06-17 14:54:59.632741 +00:00
3 rows fetched



adrci>IPS CREATE PACKAGE PROBLEM 3
Created package 1 based on problem id 3, correlation level typical

adrci> IPS GENERATE PACKAGE 3 IN /software/oracle/oracle/scripts

At this point, the zip file is created.
In addition, it is possible to see older incidents.

adrci> show incident

ADR Home = /software/oracle/diag/rdbms/igt/igt:
*************************************************************************
INCIDENT_ID          PROBLEM_KEY                                                 CREATE_TIME
-------------------- ----------------------------------------------------------- ----------------------------------------
913422               ORA 1578                                                    2018-08-01 11:07:53.855202 +00:00
913414               ORA 1578                                                    2018-08-01 11:08:15.318826 +00:00
913382               ORA 1578                                                    2018-08-01 11:08:33.824501 +00:00
913366               ORA 1578                                                    2018-08-01 11:08:31.296714 +00:00
913334               ORA 1578                                                    2018-08-01 11:08:23.704473 +00:00
910674               ORA 1578                                                    2018-06-26 05:51:44.996466 +00:00
910634               ORA 1578                                                    2018-06-26 05:47:56.707714 +00:00
910626               ORA 1578                                                    2018-06-26 05:50:22.140367 +00:00
910603               ORA 1578                                                    2018-06-26 05:51:41.576405 +00:00
910602               ORA 1578                                                    2018-06-26 05:50:37.070947 +00:00
908392               ORA 1578                                                    2018-06-26 03:44:54.058518 +00:00
908377               ORA 1578                                                    2018-06-26 03:45:20.038519 +00:00
908376               ORA 1578                                                    2018-06-26 03:43:30.007570 +00:00
905705               ORA 1578                                                    2018-06-25 16:41:19.797776 +00:00
905698               ORA 1578                                                    2018-06-25 16:40:51.885913 +00:00
905697               ORA 1578                                                    2018-06-25 16:40:10.247187 +00:00
905673               ORA 1578                                                    2018-06-25 16:41:51.501605 +00:00
905657               ORA 1578                                                    2018-06-25 16:40:23.967756 +00:00
903068               ORA 1578                                                    2018-06-25 07:29:38.796384 +00:00
903044               ORA 1578                                                    2018-06-25 07:29:15.884086 +00:00
903036               ORA 1578                                                    2018-06-25 07:29:57.106602 +00:00
903028               ORA 1578                                                    2018-06-25 07:28:12.459483 +00:00
903020               ORA 1578                                                    2018-06-25 07:28:31.065843 +00:00
900614               ORA 1578                                                    2018-06-25 05:01:40.162611 +00:00
900606               ORA 1578                                                    2018-06-25 05:03:02.017225 +00:00
900598               ORA 1578                                                    2018-06-25 05:00:27.467744 +00:00
900590               ORA 1578                                                    2018-06-25 04:57:42.799013 +00:00
900542               ORA 1578                                                    2018-06-25 05:03:11.090605 +00:00
898223               ORA 1578                                                    2018-06-25 03:49:49.148526 +00:00
898215               ORA 1578                                                    2018-06-25 03:51:04.810199 +00:00
898184               ORA 1578                                                    2018-06-25 03:51:17.896126 +00:00
898183               ORA 1578                                                    2018-06-25 03:49:47.582633 +00:00
898167               ORA 1578                                                    2018-06-25 03:53:04.846327 +00:00
895739               ORA 1578                                                    2018-06-24 14:34:45.511852 +00:00
895731               ORA 1578                                                    2018-06-24 14:34:45.511682 +00:00
895683               ORA 1578                                                    2018-06-24 14:34:48.223735 +00:00
895659               ORA 1578                                                    2018-06-24 14:34:45.604276 +00:00
895643               ORA 1578                                                    2018-06-24 14:34:47.860703 +00:00
893209               ORA 1578                                                    2018-06-24 08:22:31.437230 +00:00
893201               ORA 1578                                                    2018-06-24 08:23:09.218288 +00:00
893193               ORA 1578                                                    2018-06-24 08:22:12.367569 +00:00
893137               ORA 1578                                                    2018-06-24 08:23:05.790164 +00:00
893121               ORA 1578                                                    2018-06-24 08:23:04.021084 +00:00
890783               ORA 1578                                                    2018-06-24 07:49:24.643469 +00:00
890743               ORA 1578                                                    2018-06-24 07:49:29.911364 +00:00
890720               ORA 1578                                                    2018-06-24 07:49:00.292962 +00:00
890719               ORA 1578                                                    2018-06-24 07:48:58.370655 +00:00
890711               ORA 1578                                                    2018-06-24 07:48:50.150749 +00:00
888363               ORA 1578                                                    2018-06-24 07:23:33.911818 +00:00
888362               ORA 1578                                                    2018-06-24 07:23:26.754356 +00:00
50 rows fetched

I did not use the IPS tool eventually.
Because Option B was simpler.



============================
Options to identify the Corruption
============================
Using SQL

SELECT FILE#, 
       BLOCK#, 
       BLOCKS, 
       TO_CHAR(CORRUPTION_CHANGE#) CORRUPTION_CHANGE#, 
       CORRUPTION_TYPE 
  FROM V$DATABASE_BLOCK_CORRUPTION;

FILE# BLOCK# BLOCKS CORRUPTION_CHANGE#        CORRUPTION_TYPE
----- ------ ------ ------------------------- --------------------
    5 110573      1 15056799588275            CORRUPT


SET PAGESIZE 2000
SET LINESIZE 280 

SELECT e.owner, 
       e.segment_type, 
       e.segment_name, 
       e.partition_name, 
       c.file#,
       greatest(e.block_id, c.block#) corr_start_block#,
       least(e.block_id+e.blocks-1, c.block#+c.blocks-1) corr_end_block#,
       least(e.block_id+e.blocks-1, c.block#+c.blocks-1),
- greatest(e.block_id, c.block#) + 1 blocks_corrupted
       corruption_type description
  FROM DBA_EXTENTS e, 
       V$DATABASE_BLOCK_CORRUPTION c
 WHERE e.file_id = c.file#
   AND e.block_id <= c.block# + c.blocks - 1
   AND e.block_id + e.blocks - 1 >= c.block#
UNION
SELECT s.owner, 
       s.segment_type, 
       s.segment_name, 
       s.partition_name, 
       c.file#,
       header_block corr_start_block#,
       header_block corr_end_block#,
       1 blocks_corrupted,
       corruption_type||' Segment Header' description
  FROM DBA_SEGMENTS S, 
       V$DATABASE_BLOCK_CORRUPTION C
 WHERE s.header_file = c.file#
   AND s.header_block between c.block# and c.block# + c.blocks - 1
UNION
SELECT null owner, 
       null segment_type, 
       null segment_name, 
       null partition_name, 
       c.file#,
       greatest(f.block_id, c.block#) corr_start_block#,
       least(f.block_id+f.blocks-1, c.block#+c.blocks-1) corr_end_block#,
       least(f.block_id+f.blocks-1, c.block#+c.blocks-1)
 - greatest(f.block_id, c.block#) + 1 blocks_corrupted,
       'Free Block' description
  FROM DBA_FREE_SPACE F, 
       V$DATABASE_BLOCK_CORRUPTION C
 WHERE f.file_id = c.file#
   AND f.block_id <= c.block# + c.blocks - 1
   AND f.block_id + f.blocks - 1 >= c.block#
ORDER BY file#, corr_start_block#;

This query pointed out, that the corruption was in an index of an application table

============================
Options to fix Corruption
============================
Option A. Rebuild Index Online
In Case the index is corrupted, best option would be to use ALTER INDEX my_index REBUILD ONLINE.
When using REBULD INDEX - Oracle would use data in the Index to the Rebuild.
But when using ALTER INDEX REBUILD ONLINE Oracle would use data in the Table to the Rebuild.



Option B. IPS - Incident Packaging Service
Oracle has introduced several new Diagnosability features in 11g. 
One of them is Incident Packaging Service (IPS). 
IPS uses rules to correlate all relevant dumps and traces from ADR for a given problem and allows you to package them to ship to Oracle Support.