Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

PL/SQL & T-SQL (Oracle and SQL Server)  Module 12 – Triggers in Oracle

Compound triggers and the mutating-table error

See why a row trigger that reads its own table fails with ORA-04091, enforce a cross-row rule with a compound trigger, and write DDL and logon triggers safely.

  • Advanced
  • 15 minutes
  • Examples run with oracle-free 23.26.3.0.0, mssql 17.0.5005.3, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 6 of 15 minutes free
Free: A row trigger cannot read the table its statement is changing · A compound trigger records per row and checks once. With a pass: 2 in-depth parts and 3 interview answers, about 9 minutes.

What you will learn

  • Explain why a row trigger that queries its own table raises ORA-04091
  • Rewrite a cross-row rule as a compound trigger that records rows and checks once per statement
  • Write a schema DDL trigger and a logon trigger that cannot lock users out
  • Show why an autonomous transaction hides the mutating-table error instead of fixing it

Before you start

What you’ll learn · Before you start

What you will learn

  • Explain why a row trigger that queries its own table raises ORA-04091
  • Rewrite a cross-row rule as a compound trigger that records rows and checks once per statement
  • Write a schema DDL trigger and a logon trigger that cannot lock users out
  • Show why an autonomous transaction hides the mutating-table error instead of fixing it

Before you start

On this page

A row trigger that keeps each customer’s accounts at or below 50,000 rupees by summing their balances fails on the first UPDATE it meets: 10 % interest on the 3 savings accounts stops with ORA-04091, the mutating table: A table that a DML statement is in the middle of changing. A row-level trigger fired by that statement may neither query nor change it; trying raises ORA-04091. error, although every customer would stay under the cap. The trigger read the table its own statement was changing, which Oracle forbids.

You will see why Oracle raises the mutating-table error for that read, rewrite the rule as a compound trigger that records rows as they change and checks the finished result once, and meet the triggers that fire for schema changes and logons.

A row trigger cannot read the table its statement is changing

The cap spans rows: a CHECK constraint sees one row, but the rule is about the sum of a customer’s accounts. The first attempt is a row trigger on accounts that runs SELECT SUM(balance) for the customer of each changed row and raises an error above 50,000:

mutating_table.sql: a row trigger that sums its own table SQL · mutating/mutating_table.sql
-- Engine: Oracle AI Database 26ai Free, Release 23.26.3.0.0; user LAB in FREEPDB1. Client: SQL*Plus or SQLcl.
-- A cross-row rule written as a row trigger: a customer's accounts together may hold at most 50000 rupees. The
-- trigger sums the customer's balances in the very table its statement is changing, so Oracle raises ORA-04091
-- and rolls back both the trigger's work and the UPDATE. examples/model/mutating_model.py prints the totals and
-- outcomes described below.
SET SERVEROUTPUT ON

CREATE OR REPLACE TRIGGER accounts_cap_row
  AFTER INSERT OR UPDATE OF balance ON accounts
  FOR EACH ROW
DECLARE
  v_total accounts.balance%TYPE;
BEGIN
  SELECT SUM(balance) INTO v_total     -- reads the table this statement is changing
    FROM accounts
   WHERE customer_id = :NEW.customer_id;
  IF v_total > 50000 THEN
    RAISE_APPLICATION_ERROR(-20040, 'Customer ' || :NEW.customer_id || ' would hold ' || v_total);
  END IF;
END;
/

-- 10 % interest on the 3 savings accounts: every customer stays under the cap, yet the statement fails.
UPDATE accounts SET balance = balance * 1.10 WHERE kind = 'savings';
-- Expected:
-- ORA-04091: table LAB.ACCOUNTS is mutating, trigger/function may not see it
-- (followed by ORA-06512 and ORA-04088 lines that name LAB.ACCOUNTS_CAP_ROW)

SELECT account_id, balance FROM accounts ORDER BY account_id;
-- Expected: 2500, 18000, 760.25 and 41250.5: the UPDATE was rolled back along with the trigger's work

DROP TRIGGER accounts_cap_row;
mutating_table.sql, run in the lab Python · mutating_table_session.py
"""mutating_table.sql in the lab: SQL*Plus runs it as LAB on a freshly loaded practice schema."""

from oracle_session import Lab

with Lab() as lab:
    lab.sqlplus("mutating/mutating_table.sql", errors=1)

Output

$ sqlplus -s lab@//localhost/FREEPDB1 @mutating/mutating_table.sql
Trigger created.

UPDATE accounts SET balance = balance * 1.10 WHERE kind = 'savings'
       *
ERROR at line 1:
ORA-04091: table LAB.ACCOUNTS is mutating, trigger/function may not see it
ORA-06512: at "LAB.ACCOUNTS_CAP_ROW", line 4
ORA-04088: error during execution of trigger 'LAB.ACCOUNTS_CAP_ROW'
Help: https://docs.oracle.com/error-help/db/ora-04091/



ACCOUNT_ID    BALANCE
---------- ----------
         1       2500
         2      18000
         3     760.25
         4    41250.5


Trigger dropped.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 mutating_table_session.py

Oracle’s trigger restrictions page calls a table mutating while a DML statement is changing it, and a row-level trigger fired by that statement may neither query nor change it. The attempt raises ORA-04091, and the database rolls back both the trigger’s work and the triggering statement, so the 3 savings balances stay as they were.

The reason is the half-changed table. Step through customer 1’s sweep, one UPDATE that empties the savings account and adds 32,000 to the current account:

Interactive · step by step One UPDATE of 2 rows, read halfway through Step through the sweep in both row orders and watch the total a row trigger would see after each row.
One UPDATE of 2 rows, read halfway through, step 5 of 5. Showing UPDATE accounts, Row trigger (ORA-04091), accounts (back to 20500). In focus: Row trigger.UPDATE accounts2 rowsRow triggerORA-04091accountsback to 20500

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. One UPDATE, one allowed result. Customer 1's savings of 2500 is swept to 0 and the current account gets 32000 more, a salary of 29500 plus the swept 2500. When the statement ends, customer 1 holds 50000, exactly the cap.
  2. Account 1 first. If the database changes account 1 first, the table briefly holds 0 plus 18000. A row trigger that summed customer 1 now would see 18000, within the cap.
  3. Then account 2. After the second row the table holds 0 plus 50000, the final result. In this order both reads are within the cap, and the check passes.
  4. The other order. If the database changes account 2 first, the table briefly holds 2500 plus 50000. The trigger would see 52500, over the cap, and reject a statement whose result is allowed.
  5. Why Oracle refuses the read. SQL does not promise an order for the rows of one statement, so a half-changed total belongs to no moment a user could see. Oracle forbids the read with ORA-04091 and rolls back the trigger's work and the statement.

Read halfway, the same statement shows 18,000 in one row order and 52,500 in the other, while its result is 50,000. SQL does not fix the order in which one statement changes its rows, so a check made between two rows could pass or fail by chance. Oracle refuses the read instead.

Checkpoint

Check: Why a row trigger may not read its own table

  1. Question 1 of 3 An AFTER UPDATE row trigger on accounts runs SELECT SUM(balance) FROM accounts. What happens when an UPDATE of accounts fires it?

    Choose one answer.

    Show the answer: Question 1 of 3

    Answer: ORA-04091, because a row trigger may not query the table its statement is changing

    A row trigger fired by a DML statement can neither query nor change the table that statement is changing.

  2. Question 2 of 3 The sweep sets account 1 (2500) to 0 and adds 32000 to account 2 (18000). If account 2 changed first, what total would a row trigger see for customer 1 after that first row?

    Type a number.

    Show the answer: Question 2 of 3

    Answer: 52500

    2500 is still in account 1 and account 2 already holds 50000, so the halfway total is 52500, over the cap, for a statement whose result is exactly 50000.

  3. Question 3 of 3 The 10 % interest UPDATE fails with ORA-04091 in the row trigger. What is left in accounts?

    Choose one answer.

    Show the answer: Question 3 of 3

    Answer: The balances as they were, because the trigger's work and the UPDATE are both rolled back

    ORA-04091 rolls back the trigger and its statement, and control returns to the caller.

A compound trigger records per row and checks once

A compound trigger: One trigger with up to four timing-point sections, before and after the statement and before and after each row, that share variables living only for the triggering statement. is one trigger with up to four timing-point sections, BEFORE STATEMENT, BEFORE EACH ROW, AFTER EACH ROW and AFTER STATEMENT, whose variables live for one triggering statement. Oracle’s DML triggers page names avoiding the mutating-table error as one of two common uses of compound triggers. The fix splits the work: the row section records which customers changed, and the statement section, which the restriction does not cover, sums those customers once the table is whole again:

Interactive · step by step A compound trigger records per row and checks once Step through the sections of one compound trigger as two statements run, and watch what it records and what it reads.
A compound trigger records per row and checks once, step 5 of 5. Showing UPDATE accounts (deposit 10000), Compound trigger (ORA-20040), accounts (back to 41250.50). In focus: Compound trigger, accounts.UPDATE accountsdeposit 10000Compound triggerORA-20040accountsback to 41250.50

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. State starts with the statement. When the UPDATE starts, the trigger's declared variables are created empty. They belong to this one statement and are dropped when it ends, even when it fails.
  2. AFTER EACH ROW only records. For account 1 the row section notes customer 1 and does not query accounts, so the mutating-table rule is never touched. For account 2 it notes customer 1 again, which changes nothing.
  3. AFTER STATEMENT reads the result. Once every row has changed, the statement section sums customer 1 once. The restriction covers row-level code only, so the read is allowed, and it sees the result the user will see, 50000. The statement completes.
  4. A deposit that breaks the cap. A new statement adds 10000 to account 4. The state starts empty again, the row section notes customer 4, and the statement section finds 51250.50, above the cap.
  5. The error rolls the deposit back. The statement section raises ORA-20040 with RAISE_APPLICATION_ERROR, and the UPDATE fails as a whole, so account 4 keeps 41250.50. The state is dropped with the statement.

The model runs the row trigger that fails, the half-changed reads of the sweep and the compound approach, which passes the interest credit (20,750.00, 836.28 and 45,375.55) and the sweep (50,000.00) and rejects the deposit that would take customer 4 to 51,250.50. Press Edit, change an amount in STATEMENTS, and run it again:

A half-changed table, and a compound trigger that checks once Python · model/mutating_model.py
"""Why a row trigger may not read its own table mid-statement, and how a compound trigger checks once.

The rule: a customer's accounts together may hold at most 50000 rupees. mutating_table.sql enforces it
with a row trigger that sums the customer's balances, and Oracle refuses that read with ORA-04091. This
model shows why the read is refused: it applies one UPDATE row by row, in both orders a database might
use, and prints the total a row trigger would see after each row. Then it runs the compound-trigger
approach: record each touched customer per row, and sum their balances once, after the statement.
Edit STATEMENTS (other amounts, other accounts) and run it again.
"""

from decimal import Decimal, ROUND_HALF_UP

CAP = Decimal("50000.00")
ACCOUNTS = {  # account_id: (customer_id, kind, balance), the practice data
    1: (1, "savings", Decimal("2500.00")),
    2: (1, "current", Decimal("18000.00")),
    3: (2, "savings", Decimal("760.25")),
    4: (4, "savings", Decimal("41250.50")),
}


def interest(balance):
    return (balance * Decimal("1.10")).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)


# (what the UPDATE does, {account_id: new balance from old balance}) in the order the rows are listed
STATEMENTS = [
    ("10 % interest on the 3 savings accounts", {1: interest, 3: interest, 4: interest}),
    ("customer 1: sweep savings into current, plus a 29500 salary",
     {1: lambda b: Decimal("0.00"), 2: lambda b: b + Decimal("32000.00")}),
    ("deposit 10000 into account 4", {4: lambda b: b + Decimal("10000.00")}),
]


def total(table, customer):
    return sum((bal for cust, _, bal in table.values() if cust == customer), Decimal("0.00"))


print("1. A row trigger that sums the customer's balances")
for text, changes in STATEMENTS:
    print(f"  {text}: ORA-04091 at the first row; the UPDATE is rolled back")

print("2. If a row trigger could read the half-changed table: the sweep, in two row orders")
text, changes = STATEMENTS[1]
for order in ([1, 2], [2, 1]):
    table = dict(ACCOUNTS)
    seen = []
    for account in order:
        cust, kind, bal = table[account]
        table[account] = (cust, kind, changes[account](bal))
        seen.append(total(table, cust))
    verdicts = ["over the cap" if t > CAP else "within" for t in seen]
    steps = ", then ".join(f"{t} ({v})" for t, v in zip(seen, verdicts))
    print(f"  rows {order[0]} then {order[1]}: the trigger sees {steps}")

print("3. A compound trigger: record customers in AFTER EACH ROW, sum them in AFTER STATEMENT")
for text, changes in STATEMENTS:
    table = dict(ACCOUNTS)
    touched = []
    for account, change in changes.items():  # AFTER EACH ROW
        cust, kind, bal = table[account]
        table[account] = (cust, kind, change(bal))
        if cust not in touched:
            touched.append(cust)
    totals = {c: total(table, c) for c in touched}  # AFTER STATEMENT: the table is whole again
    over = [c for c, t in totals.items() if t > CAP]
    shown = ", ".join(f"customer {c} {t}" for c, t in totals.items())
    outcome = f"ORA-20040 for customer {over[0]}; the UPDATE is rolled back" if over else "passes"
    print(f"  {text}: {shown} -> {outcome}")

Output

1. A row trigger that sums the customer's balances
  10 % interest on the 3 savings accounts: ORA-04091 at the first row; the UPDATE is rolled back
  customer 1: sweep savings into current, plus a 29500 salary: ORA-04091 at the first row; the UPDATE is rolled back
  deposit 10000 into account 4: ORA-04091 at the first row; the UPDATE is rolled back
2. If a row trigger could read the half-changed table: the sweep, in two row orders
  rows 1 then 2: the trigger sees 18000.00 (within), then 50000.00 (within)
  rows 2 then 1: the trigger sees 52500.00 (over the cap), then 50000.00 (within)
3. A compound trigger: record customers in AFTER EACH ROW, sum them in AFTER STATEMENT
  10 % interest on the 3 savings accounts: customer 1 20750.00, customer 2 836.28, customer 4 45375.55 -> passes
  customer 1: sweep savings into current, plus a 29500 salary: customer 1 50000.00 -> passes
  deposit 10000 into account 4: customer 4 51250.50 -> ORA-20040 for customer 4; the UPDATE is rolled back

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 mutating_model.py

Four related tools are worth knowing by name. A plain AFTER statement trigger may also read the finished table, but unlike compound triggers it cannot tell which customers changed, so it must check them all. A temporary table, written by a row trigger and read by a statement trigger, is the older documented route to the same result. system trigger: A trigger created on a schema or on the database that fires for a DDL statement, such as CREATE or DROP, or for a database event, such as a logon, rather than for changed rows. fire for DDL in a schema or for database events such as a logon, not for changed rows. An autonomous transaction: A transaction of its own that a routine marked PRAGMA AUTONOMOUS_TRANSACTION runs while the main transaction waits; its COMMIT keeps its changes whatever the caller does afterwards. More in “Error logging, backtraces and autonomous logs” silences ORA-04091 in a row trigger, but its query reads only committed data, so the check works on the wrong totals.

Key idea

A row trigger sees its table halfway through the statement, so it may not read it. Record what changed, row by row, and check the finished result once, after the statement.

Checkpoint

Check: Record per row, check once after the statement

  1. Question 1 of 3 Which section of a compound trigger records the customer of each changed row?

    Choose one answer.

    Show the answer: Question 1 of 3

    Answer: AFTER EACH ROW

    Rows are recorded in a row section and checked in AFTER STATEMENT.

  2. Question 2 of 3 Why may AFTER STATEMENT query accounts when AFTER EACH ROW may not?

    Choose one answer.

    Show the answer: Question 2 of 3

    Answer: The mutating-table restriction covers row-level code, and by then the statement has changed every row

    Record in the row section, read in the statement section.

  3. Question 3 of 3 The sweep runs, then the deposit into account 4 in a second statement. What does the compound trigger's record hold when the second statement starts?

    Choose one answer.

    Show the answer: Question 3 of 3

    Answer: Nothing; the state is created for each triggering statement and dropped when it ends

    Each triggering statement gets fresh state, even after an error.

The 50,000-rupee cap as a compound trigger, tested on six statements Pro: opens with a Pro, Premium or Ultimate pass

The compound trigger below enforces the cap for every insert and every change of a balance or an owner, and six statements test it on the practice accounts. Oracle’s rules for each section explain the way it is written.

In this part:

  • Recording customers in an associative array keyed by id
  • Worked numbers: six statements through the trigger
  • A race between two sessions, with numbers
  • The rules each section obeys

3 runnable examples (Python, SQL) · about 8 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

DDL and logon triggers, and why an autonomous transaction only hides ORA-04091 Pro: opens with a Pro, Premium or Ultimate pass

Triggers can fire for schema changes and logons as well as for rows, and each kind can be written so that a mistake in it stays harmless. An autonomous transaction, a common workaround for ORA-04091, is then shown to check the wrong totals.

In this part:

  • A schema trigger that logs every DDL statement
  • A logon trigger that fails can lock users out: reproduce it in a lab
  • An autonomous transaction hides ORA-04091 and checks the wrong numbers

5 runnable examples (Python, SQL) · about 5 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Practise: predict what each trigger does

Each question shows a trigger and a statement. Decide what the trigger may read and when, then read why each answer is right or wrong:

Practice that adapts to you

Practise this lesson

7 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 7 Which code does the mutating-table restriction apply to?

    Choose one answer.

    Show the answer: Question 1 of 7

    Answer: Row-level DML triggers fired by the statement that is changing the table

    Row triggers fired by the change may not read or change the table; statement-level code may.

  2. Question 2 of 7 The interest UPDATE would leave every customer under the cap, yet it fails with ORA-04091. Why?

    Choose one answer.

    Show the answer: Question 2 of 7

    Answer: The error is about reading a changing table, not about the cap; the row trigger fails before it checks anything

    ORA-04091 is raised by the read, so even a statement that keeps the rule fails.

  3. Question 3 of 7 Why would a row trigger's halfway total be unreliable even if Oracle allowed the read?

    Choose one answer.

    Show the answer: Question 3 of 7

    Answer: SQL does not fix the order in which one statement changes its rows, so the total depends on chance

    A check between two rows of one statement can pass or fail by chance, so Oracle refuses it.

  4. Question 4 of 7 A teammate catches ORA-04091 inside the row trigger with an exception handler that skips the check. The UPDATE now succeeds. What has happened to the cap?

    Choose one answer.

    Show the answer: Question 4 of 7

    Answer: It is never checked any more, because every UPDATE takes the handler path

    Hiding the error drops the rule; move the check to a statement section instead.

  5. Question 5 of 7 Which list holds exactly the timing-point sections a compound DML trigger on a table may have?

    Choose one answer.

    Show the answer: Question 5 of 7

    Answer: BEFORE STATEMENT, BEFORE EACH ROW, AFTER EACH ROW, AFTER STATEMENT

    Up to four sections share the trigger's state for one statement.

  6. Question 6 of 7 How does the AFTER STATEMENT section know which customers to check?

    Choose one answer.

    Show the answer: Question 6 of 7

    Answer: It reads the record the AFTER EACH ROW section filled, because all sections share the trigger's variables

    Record in a row section, read the record in the statement section.

  7. Question 7 of 7 A new rule says the balances of all accounts together may not pass 50,000,000 rupees. Which trigger is enough?

    Choose one answer.

    Show the answer: Question 7 of 7

    Answer: A plain AFTER statement trigger that sums the whole table once

    Record per row only when the check is per group; a whole-table rule fits a plain statement trigger.

Interview questions

Warm-up (2–5 years): what is a mutating table, and which triggers does the rule apply to?

A table that a DML statement is in the middle of changing. The rule binds the row-level triggers fired by that statement: they may not query or change the table, and trying raises ORA-04091, which rolls back the trigger and the statement. Statement-level triggers and a compound trigger’s statement sections may read it, because no row is halfway through a change then.

Three longer questions follow, on a cap that fails in its first test, a fix that made the error disappear, and a logon trigger to add safely; work out your answer before you open each first step:

  1. Mid to senior

    A row trigger on accounts keeps each customer's total at or below a cap with SELECT SUM(balance) for the row's customer. Its first test, a 10 % interest UPDATE, fails with ORA-04091 although no customer goes over. Explain the error and rewrite the check.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to staff

    To get past ORA-04091, a teammate added PRAGMA AUTONOMOUS_TRANSACTION to the row trigger that enforces a balance cap. The error is gone and the existing tests pass. What would you check, and what would you ship instead?

    Model answer, Pro. First step and follow-ups

  3. Senior to staff

    You must add an AFTER LOGON ON DATABASE trigger that records every new session. How do you make sure a bug in it cannot lock users out, and how do you test it before anyone depends on it?

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What is a mutating table, and what may a row trigger not do with it?

    A table that a DML statement is changing. A row-level trigger fired by that statement may neither query nor change it; trying raises ORA-04091, and the database rolls back both the trigger's work and the triggering statement.

  2. Why does Oracle refuse the read rather than allow it?

    The trigger would see the table half changed, and SQL does not fix the order in which one statement changes its rows: a sweep whose result is 50,000 shows 18,000 in one row order and 52,500 in the other, so a check made between two rows could pass or fail by chance.

  3. Why can a CHECK constraint not enforce a cap on the sum of a customer's accounts?

    A CHECK constraint sees one row, and the rule spans rows: it is about the total of all the customer's accounts.

  4. Which sections can a compound trigger have, and how long do its variables live?

    Up to four timing points in one trigger: BEFORE STATEMENT, BEFORE EACH ROW, AFTER EACH ROW and AFTER STATEMENT. Its variables live for one triggering statement, so each statement starts with an empty record.

  5. How does a compound trigger enforce a cross-row rule?

    The row section records which keys changed, and the AFTER STATEMENT section, which the restriction does not cover, sums just those keys once the table is whole again.

  6. What can a plain AFTER statement trigger not do that a compound trigger can?

    It may also read the finished table, but it cannot tell which keys changed, so it has to check them all.

  7. Why is an autonomous transaction not a fix for ORA-04091?

    It silences the error, but its query reads only committed data, so the check works on the wrong totals.

  8. What do system triggers fire for?

    For DDL in a schema or for database events such as a logon, not for changed rows.

Key takeaways

  • A row trigger may not query or change the table its statement is changing, and trying raises ORA-04091 and rolls the statement back.
  • Read halfway, one statement shows totals that depend on row order, such as 18,000 or 52,500 for a result of 50,000.
  • A compound trigger records what changed in its row sections and checks the finished table once, in AFTER STATEMENT.
  • A compound trigger’s variables live for one triggering statement, so each statement starts with an empty record.
  • System triggers fire for DDL and database events, and an autonomous transaction silences ORA-04091 without making the check right.

Terms in this lesson

compound trigger
One trigger with up to four timing-point sections, before and after the statement and before and after each row, that share variables living only for the triggering statement.
mutating table
A table that a DML statement is in the middle of changing. A row-level trigger fired by that statement may neither query nor change it; trying raises ORA-04091.
system trigger
A trigger created on a schema or on the database that fires for a DDL statement, such as CREATE or DROP, or for a database event, such as a logon, rather than for changed rows.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.