Skip to main content

How to skip transaction in Logical Standby

Skipping a stalled transaction on an Oracle Logical Standby database

Introduction

An Oracle Logical Standby database can be used for write operations if required, to run reports or test statements. However, if the same transaction is executed on the primary, it will create a conflict and SQL Apply will abort on the standby, resulting in the primary and standby falling out of sync. In this article, we will see how to resolve such an issue by skipping the transaction.

Prerequisites

  • Logical standby should be configured and in sync.

Environment Used in this Guide

Server Primary Standby
Hostname drdb.oraeasy.com dcdb.oraeasy.com
OS OEL 9 OEL 9
SID ORADBDR ORADBDC
Service Name ORADBDR ORADBDC

Creating and Resolving the Conflict Step by Step

Step 1: Check the Sync Between Primary and Standby

Before intentionally creating a conflict, confirm the standby is fully in sync. This establishes the clean baseline this article deliberately breaks and then repairs.


Primary:
SQL> select name,db_unique_name,database_role,open_mode from v$database; NAME DB_UNIQUE_NAME DATABASE_ROLE OPEN_MODE --------- ------------------------------ ---------------- -------------------- DRDB oradbdr PRIMARY READ WRITE SQL> select max(sequence#) from v$archived_log; MAX(SEQUENCE#) -------------- 44 Standby:
SQL> select name,db_unique_name,database_role,open_mode from v$database; NAME DB_UNIQUE_NAME DATABASE_ROLE OPEN_MODE --------- ------------------------------ ---------------- -------------------- ORADB oradbdc LOGICAL STANDBY READ WRITE SQL> select sequence#,first_change#,next_change#,timestamp,applied,blocks,block_size from dba_logstdby_log; SEQUENCE# FIRST_CHANGE# NEXT_CHANGE# TIMESTAMP APPLIED BLOCKS BLOCK_SIZE ---------- ------------- ------------ --------- -------- ---------- ---------- 27 2497644 2497970 20-DEC-25 YES 38333 512 28 2497970 2497978 20-DEC-25 YES 7 512 29 2497978 2501570 20-DEC-25 YES 16436 512 30 2501570 2503802 20-DEC-25 YES 10108 512 31 2503802 2503810 20-DEC-25 YES 14 512 32 2503810 2506655 20-DEC-25 YES 2482 512 33 2506655 2508558 20-DEC-25 YES 1114 512 34 2508558 2511064 20-DEC-25 YES 12143 512 35 2511064 2512631 21-DEC-25 YES 1190 512 36 2512631 2513019 21-DEC-25 YES 374 512 37 2513019 2517084 21-DEC-25 YES 42537 512 38 2517084 2519228 21-DEC-25 YES 1443 512 39 2519228 2521270 21-DEC-25 YES 1234 512 40 2521270 2523427 21-DEC-25 YES 1428 512 41 2523427 2526569 21-DEC-25 YES 11094 512 42 2526569 2528515 21-DEC-25 YES 1071 512 43 2528515 2530374 21-DEC-25 YES 1003 512 44 2530374 2532229 21-DEC-25 CURRENT 1118 512

Sequence 44 marked CURRENT on the standby confirms it's fully caught up with the primary before the conflicting transaction is introduced.

Step 2: On the Standby, Create a User

To set up the scenario, create a user directly on the standby first. Since a logical standby is open read/write, it accepts this write locally without issue.


SQL> select name,db_unique_name,database_role,open_mode from v$database;

NAME      DB_UNIQUE_NAME                 DATABASE_ROLE    OPEN_MODE
--------- ------------------------------ ---------------- --------------------
ORADB     oradbdc                        LOGICAL STANDBY  READ WRITE

SQL> create user abhi identified by Your@Password;

User created.

SQL> select username from dba_users where username like 'AB%';

USERNAME
--------------
ABHI

The ABHI user now existing locally on the standby, but not yet on the primary, is what will collide once the primary's own version of this user tries to replicate in.

Step 3: On the Primary, Create the Same User and Switch Logs

Create the same username on the primary and force log switches so the conflicting CREATE USER statement gets shipped to the standby.


SQL> create user abhi identified by Your@Password;

User created.

SQL> alter system switch logfile;

System altered.

SQL> /

System altered.

SQL> select max(sequence#) from v$archived_log;

MAX(SEQUENCE#)
--------------
            46

The archived log sequence advancing to 46 confirms the conflicting transaction has been generated on the primary and is on its way to the standby.

Step 4: On the Standby, Check the Sync

Check the standby's sync status again. Sequences 45 and 46 should now show APPLIED = NO, since SQL Apply can't process the conflicting CREATE USER statement.


SQL> select sequence#,first_change#,next_change#,timestamp,applied,blocks,block_size from dba_logstdby_log;

 SEQUENCE# FIRST_CHANGE# NEXT_CHANGE# TIMESTAMP APPLIED      BLOCKS BLOCK_SIZE
---------- ------------- ------------ --------- -------- ---------- ----------
        27       2497644      2497970 20-DEC-25 YES           38333        512
        28       2497970      2497978 20-DEC-25 YES               7        512
        29       2497978      2501570 20-DEC-25 YES           16436        512
        30       2501570      2503802 20-DEC-25 YES           10108        512
        31       2503802      2503810 20-DEC-25 YES              14        512
        32       2503810      2506655 20-DEC-25 YES            2482        512
        33       2506655      2508558 20-DEC-25 YES            1114        512
        34       2508558      2511064 20-DEC-25 YES           12143        512
        35       2511064      2512631 21-DEC-25 YES            1190        512
        36       2512631      2513019 21-DEC-25 YES             374        512
        37       2513019      2517084 21-DEC-25 YES           42537        512
        38       2517084      2519228 21-DEC-25 YES            1443        512
        39       2519228      2521270 21-DEC-25 YES            1234        512
        40       2521270      2523427 21-DEC-25 YES            1428        512
        41       2523427      2526569 21-DEC-25 YES           11094        512
        42       2526569      2528515 21-DEC-25 YES            1071        512
        43       2528515      2530374 21-DEC-25 YES            1003        512
        44       2530374      2532229 21-DEC-25 CURRENT        1118        512
        45       2532229      2532712 21-DEC-25 NO              289        512
        46       2532712      2532724 21-DEC-25 NO               17        512

20 rows selected.

SQL> SELECT NAME, VALUE, UNIT FROM V$DATAGUARD_STATS;

NAME                             VALUE                                                            UNIT
-------------------------------- ---------------------------------------------------------------- ------------------------------
transport lag                    +00 00:00:00                                                     day(2) to second(0) interval
apply lag                        +00 00:05:39                                                     day(2) to second(0) interval
apply finish time                                                                                 day(2) to second(3) interval
estimated startup time           31                                                               second

From this output we can see that logs are transferred but not applied. The apply lag climbing to over 5 minutes, with the two most recent sequences unapplied, confirms SQL Apply has stalled on the conflict.

Step 5: Check for the Error and Problematic SQL Statement

DBA_LOGSTDBY_EVENTS records exactly what went wrong. The ORA-01920 error names the conflicting user and the statement that caused it.


Check for the apply lag error:
SQL> SELECT event_time,status,EVENT from dba_logstdby_events; EVENT_TIM STATUS EVENT --------- ---------------------------------------------------------------------- ---------------------------------------------- 21-DEC-25 ORA-16111: log mining and apply setting up 21-DEC-25 Apply LWM 2511526, HWM 2511526, SCN 2512607 21-DEC-25 ORA-16222: automatic Logical Standby retry of last action 21-DEC-25 ORA-16111: log mining and apply setting up 21-DEC-25 Apply LWM 2527314, HWM 2527314, SCN 2530373 21-DEC-25 ORA-01920: user name 'ABHI' conflicts with another user or role name create user abhi identified by '*' 20-DEC-25 ORA-16111: log mining and apply setting up 20-DEC-25 Apply LWM 0, HWM 0, SCN 0 20-DEC-25 ORA-16226: DDL skipped due to lack of support alter database commit to switchover to primary 20-DEC-25 Shutdown acknowledged 20-DEC-25 ORA-16128: User initiated stop apply successfully completed Check the problematic statement:
SQL> SELECT XIDUSN, XIDSLT, XIDSQN, EVENT FROM DBA_LOGSTDBY_EVENTS WHERE EVENT_TIME = (SELECT MAX(EVENT_TIME) FROM DBA_LOGSTDBY_EVENTS); XIDUSN XIDSLT XIDSQN EVENT ---------- ---------- ---------- -------------------------------------- 10 30 1291 create user abhi identified by '*'

The XIDUSN, XIDSLT, and XIDSQN values returned here uniquely identify the stalled transaction, which is exactly what the skip command needs in the next step.

Step 6: On the Standby, Skip the Transaction

SQL Apply must be stopped before skipping a transaction, since DBMS_LOGSTDBY.SKIP_TRANSACTION can't be issued while apply is active. The syntax is EXECUTE DBMS_LOGSTDBY.SKIP_TRANSACTION(XIDUSN_val, XIDSLT_val, XIDSQN_val);, passing the three identifiers found in the previous step.


Stop SQL apply:
SQL> ALTER DATABASE STOP LOGICAL STANDBY APPLY; Database altered. Skip the transaction:
SQL> EXECUTE DBMS_LOGSTDBY.SKIP_TRANSACTION(10, 30, 1291); PL/SQL procedure successfully completed. Start SQL apply:
SQL> ALTER DATABASE START LOGICAL STANDBY APPLY; Database altered.

Step 7: Check the Sync

With the conflicting transaction skipped, confirm the previously stuck sequences are now processing.


SQL> SELECT NAME, VALUE, UNIT FROM V$DATAGUARD_STATS;

NAME                             VALUE                                                            UNIT
-------------------------------- ---------------------------------------------------------------- ------------------------------
transport lag                    +00 00:00:00                                                     day(2) to second(0) interval
apply lag                        +00 00:08:27                                                     day(2) to second(0) interval
apply finish time                                                                                 day(2) to second(3) interval
estimated startup time           31                                                               second

SQL> select sequence#,first_change#,next_change#,timestamp,applied,blocks,block_size from dba_logstdby_log;

 SEQUENCE# FIRST_CHANGE# NEXT_CHANGE# TIMESTAMP APPLIED      BLOCKS BLOCK_SIZE
---------- ------------- ------------ --------- -------- ---------- ----------
        27       2497644      2497970 20-DEC-25 YES           38333        512
        28       2497970      2497978 20-DEC-25 YES               7        512
        29       2497978      2501570 20-DEC-25 YES           16436        512
        30       2501570      2503802 20-DEC-25 YES           10108        512
        31       2503802      2503810 20-DEC-25 YES              14        512
        32       2503810      2506655 20-DEC-25 YES            2482        512
        33       2506655      2508558 20-DEC-25 YES            1114        512
        34       2508558      2511064 20-DEC-25 YES           12143        512
        35       2511064      2512631 21-DEC-25 YES            1190        512
        36       2512631      2513019 21-DEC-25 YES             374        512
        37       2513019      2517084 21-DEC-25 YES           42537        512
        38       2517084      2519228 21-DEC-25 YES            1443        512
        39       2519228      2521270 21-DEC-25 YES            1234        512
        40       2521270      2523427 21-DEC-25 YES            1428        512
        41       2523427      2526569 21-DEC-25 YES           11094        512
        42       2526569      2528515 21-DEC-25 YES            1071        512
        43       2528515      2530374 21-DEC-25 YES            1003        512
        44       2530374      2532229 21-DEC-25 YES            1118        512
        45       2532229      2532712 21-DEC-25 YES             289        512
        46       2532712      2532724 21-DEC-25 CURRENT          17        512

20 rows selected.

Sequences 45 and 46 now showing APPLIED = YES confirm the skip resolved the stall and SQL Apply has resumed processing normally.

Step 8: Roll Back the Skip Transaction Activity (If Needed)

If a transaction was skipped in error, DBMS_LOGSTDBY.UNSKIP_TRANSACTION reverses it. DBA_LOGSTDBY_SKIP_TRANSACTION lists any transactions currently marked as skipped, which is where the identifiers for the rollback come from.


Check the skipped transaction:
SQL> select XIDUSN,XIDSLT,XIDSQN from DBA_LOGSTDBY_SKIP_TRANSACTION; XIDUSN XIDSLT XIDSQN ---------- ---------- ---------- 10 30 1291 Stop SQL apply:
SQL> ALTER DATABASE STOP LOGICAL STANDBY APPLY; Database altered. Rollback the skipped transaction:
SQL> EXECUTE DBMS_LOGSTDBY.UNSKIP_TRANSACTION(10, 30, 1291); PL/SQL procedure successfully completed. Start SQL apply:
SQL> ALTER DATABASE start LOGICAL STANDBY APPLY; Database altered.

Step 9: Verify the Sync Between Primary and Standby

A final full sync check confirms the logical standby has fully caught up after the skip-and-resume cycle covered in this article.


Primary:
SQL> alter system switch logfile; System altered. SQL> / System altered. SQL> / System altered. SQL> select max(sequence#) from v$archived_log; MAX(SEQUENCE#) -------------- 60 Standby:
SQL> select sequence#,first_change#,next_change#,timestamp,applied,blocks,block_size from dba_logstdby_log SEQUENCE# FIRST_CHANGE# NEXT_CHANGE# TIMESTAMP APPLIED BLOCKS BLOCK_SIZE ---------- ------------- ------------ --------- -------- ---------- ---------- 27 2497644 2497970 20-DEC-25 YES 38333 512 28 2497970 2497978 20-DEC-25 YES 7 512 29 2497978 2501570 20-DEC-25 YES 16436 512 30 2501570 2503802 20-DEC-25 YES 10108 512 31 2503802 2503810 20-DEC-25 YES 14 512 32 2503810 2506655 20-DEC-25 YES 2482 512 33 2506655 2508558 20-DEC-25 YES 1114 512 34 2508558 2511064 20-DEC-25 YES 12143 512 35 2511064 2512631 21-DEC-25 YES 1190 512 36 2512631 2513019 21-DEC-25 YES 374 512 37 2513019 2517084 21-DEC-25 YES 42537 512 38 2517084 2519228 21-DEC-25 YES 1443 512 39 2519228 2521270 21-DEC-25 YES 1234 512 40 2521270 2523427 21-DEC-25 YES 1428 512 41 2523427 2526569 21-DEC-25 YES 11094 512 42 2526569 2528515 21-DEC-25 YES 1071 512 43 2528515 2530374 21-DEC-25 YES 1003 512 44 2530374 2532229 21-DEC-25 YES 1118 512 45 2532229 2532712 21-DEC-25 YES 289 512 46 2532712 2532724 21-DEC-25 YES 17 512 47 2532724 2543822 21-DEC-25 YES 79388 512 48 2543822 2545842 21-DEC-25 YES 1219 512 49 2545842 2547753 21-DEC-25 YES 1092 512 50 2547753 2549659 21-DEC-25 YES 1048 512 51 2549659 2552502 21-DEC-25 YES 12624 512 52 2552502 2552509 21-DEC-25 YES 4 512 53 2552509 2552613 21-DEC-25 YES 66 512 54 2552613 2552659 21-DEC-25 YES 34 512 55 2552659 2554650 21-DEC-25 YES 1108 512 56 2554650 2556557 21-DEC-25 YES 1069 512 57 2556557 2558464 21-DEC-25 YES 1046 512 58 2558464 2561556 21-DEC-25 YES 10615 512 59 2561556 2562799 21-DEC-25 YES 697 512 60 2562799 2562807 21-DEC-25 YES 14 512

All sequences through 60 now showing APPLIED = YES confirm the standby is fully caught up with the primary after the skip-and-resume cycle.

Step 10: Useful Commands for Managing Logical Standby

For quick reference, here are the commands used throughout this article, all in one place.


Start SQL apply:
ALTER DATABASE START LOGICAL STANDBY APPLY; Stop SQL apply:
ALTER DATABASE STOP LOGICAL STANDBY APPLY; Monitor apply lag:
SELECT name, value FROM v$dataguard_stats WHERE name LIKE '%lag%'; Check the last applied redo sequence:
SELECT thread#, sequence#, applied FROM v$archived_log WHERE applied = 'YES' ORDER BY sequence# DESC; View SQL apply progress:
SELECT * FROM v$logstdby_progress; Identify SQL apply errors if any:
SELECT event_time,status,EVENT from dba_logstdby_events; Check problematic SQL statement:
SQL> SELECT XIDUSN, XIDSLT, XIDSQN, EVENT FROM DBA_LOGSTDBY_EVENTS WHERE EVENT_TIME = (SELECT MAX(EVENT_TIME) FROM DBA_LOGSTDBY_EVENTS); Skip transaction:
SQL> EXECUTE DBMS_LOGSTDBY.SKIP_TRANSACTION(XIDUSN_val, XIDSLT_val, XIDSQN_val); Check the skipped transaction:
SQL> select XIDUSN,XIDSLT,XIDSQN from DBA_LOGSTDBY_SKIP_TRANSACTION; Rollback of skipped transaction:
SQL> EXECUTE DBMS_LOGSTDBY.UNSKIP_TRANSACTION(XIDUSN, XIDSLT, XIDSQN);


Thank you for reading!

I hope this content has been helpful to you. Your feedback and suggestions are always welcome. Feel free to leave a comment or reach out with any queries.

Abhishek Shrivastava

Oracle DBA with hands-on experience managing production Data Guard, RAC, GoldenGate, and APEX environments. I write practical, tested installation and troubleshooting guides based on real deployment work.

📧 Email: oraeasyy@gmail.com
🌐 Website: www.oraeasy.com

Follow Us

Comments

↑