An athletic awards database transition table trigger audit gives school recognition staff a way to see every award record changed during a batch operation — as a complete set, before the transaction commits — and decide whether to proceed, log the evidence, or roll back. PostgreSQL’s statement-level triggers with REFERENCING OLD TABLE and NEW TABLE clauses expose the entire before-and-after snapshot of a batch change as queryable relations, letting a trigger function audit the full set, update derived recognition counts, and preserve rollback evidence rather than inspecting one row at a time.
This guide is written for athletic directors, IT administrators, and recognition program staff who manage batch updates to award records and need a structured review checkpoint before changes are published to displays. It covers the mechanics of transition relations, a numbered audit workflow, a decision table for selecting the right trigger design, and FAQs on scope, event restrictions, and rollback behavior.
When a school processes end-of-season awards in bulk — updating dozens or hundreds of award records in a single batch operation — a row-level trigger fires once for every affected row, with no natural point where staff can see the complete change set before it commits. For a batch that revises 80 athlete records across three sports, a row-level trigger fires 80 separate times. There is no built-in pause where the trigger can say: “Here are all 80 changes together — review them before committing.”
An athletic awards database transition table trigger audit changes that outcome. PostgreSQL statement-level triggers with transition relations capture every affected row into named, queryable relations — OLD TABLE for pre-change values, NEW TABLE for post-change values — that the trigger function can query as a whole before the transaction commits. This makes it possible to audit the full batch, compute the correct recognition count delta, and write rollback evidence to an audit log in a single trigger invocation — so every update that reaches a recognition display has been confirmed as correct and complete.

Each portrait card in a recognition program represents a record that was written to the database at some point — transition table triggers let staff see the complete batch of changes before any update reaches a public display
What Transition Tables Are and Why They Matter for Athletic Award Batches
PostgreSQL statement-level triggers support a REFERENCING clause that declares named transition relations for the affected row set. As documented in the official PostgreSQL CREATE TRIGGER reference: “The REFERENCING option enables collection of transition relations, which are row sets that represent the old and/or new states of the affected table.”
Two transition relation types exist:
OLD TABLE contains the pre-change version of every row affected by the triggering statement. It is available in AFTER triggers on UPDATE and DELETE events. For a batch UPDATE that revises award titles across 60 records, OLD TABLE holds all 60 original rows — the complete set of what existed before the batch ran.
NEW TABLE contains the post-change version of every row affected by the triggering statement. It is available in AFTER triggers on INSERT and UPDATE events. For the same batch UPDATE, NEW TABLE holds all 60 rows as they will appear after the update commits.
These relations are available only in statement-level (FOR EACH STATEMENT) AFTER triggers. As further described in the PostgreSQL PL/pgSQL trigger documentation, the transition table names declared in the REFERENCING clause are visible as queryable relations within the trigger function body. Row-level triggers (FOR EACH ROW) do not support transition relations — they provide OLD and NEW pseudo-records for the current row only. For a batch operation, transition relations are the only built-in mechanism that gives the trigger access to the complete change set as a queryable relation.
For school recognition programs, this distinction determines whether a trigger can catch a batch-level problem — such as a sport miscategorization applied to 40 records at once — or can only examine one record at a time. A transition table audit catches the whole batch before a single row is committed.
When award records are also maintained through a materialized view that aggregates recognition totals by sport and season, the athletic awards database materialized view refresh policy explains how refresh timing interacts with batch write operations — a related concern that transition table triggers address through a pre-commit review mechanism rather than a post-commit refresh schedule.
Step-by-Step: Transition Table Audit Workflow for Athletic Award Batches
Step 1 — Create the Audit Table
The audit table captures every row from the transition relations, along with the batch identifier, the operation type, and a review_status column that staff use to track whether the batch was approved or rolled back.
CREATE TABLE award_batch_audit (
audit_id BIGSERIAL PRIMARY KEY,
batch_id TEXT NOT NULL,
audit_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
operation TEXT NOT NULL,
award_id INTEGER,
athlete_id INTEGER,
old_award_title TEXT,
new_award_title TEXT,
old_season_year INTEGER,
new_season_year INTEGER,
old_sport TEXT,
new_sport TEXT,
review_status TEXT NOT NULL DEFAULT 'pending',
reviewed_by TEXT,
review_note TEXT
);
The batch_id column links every row in the audit table to the specific import run that generated it. The review_status column moves from pending to approved or rolled_back once staff complete the review.
Step 2 — Write the Trigger Function
The trigger function uses the transition relation names declared in the trigger’s REFERENCING clause. The aliases — old_awards and new_awards — must match what the REFERENCING clause declares exactly. Inside a PL/pgSQL trigger function, those aliases are available as queryable relations just like any database table.
CREATE OR REPLACE FUNCTION audit_award_batch()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
v_batch_id TEXT;
v_changed_count INTEGER;
BEGIN
v_batch_id := current_setting('app.current_batch_id', true);
-- Capture before-and-after state from transition relations
INSERT INTO award_batch_audit (
batch_id, operation, award_id, athlete_id,
old_award_title, new_award_title,
old_season_year, new_season_year,
old_sport, new_sport
)
SELECT
v_batch_id,
TG_OP,
COALESCE(n.award_id, o.award_id),
COALESCE(n.athlete_id, o.athlete_id),
o.award_title,
n.award_title,
o.season_year,
n.season_year,
o.sport,
n.sport
FROM new_awards n
FULL OUTER JOIN old_awards o USING (award_id);
-- Enforce a batch size threshold before committing
SELECT COUNT(*) INTO v_changed_count FROM new_awards;
IF v_changed_count > 200 THEN
RAISE EXCEPTION
'Batch exceeds 200-row review threshold: % rows changed. Roll back and re-submit in smaller segments.',
v_changed_count;
END IF;
RETURN NULL; -- Statement-level trigger functions must return NULL
END;
$$;
RETURN NULL is required for statement-level trigger functions — they do not return a row value. The RAISE EXCEPTION call aborts the entire transaction, including the batch DML that fired the trigger, if the change set exceeds the configured threshold. This batch-size gate prevents unreviewed bulk changes from reaching the production award table.
Step 3 — Create the Statement-Level AFTER Trigger
The REFERENCING clause declares the transition relation aliases. OLD TABLE AS old_awards makes the pre-change row set queryable under that name inside the function; NEW TABLE AS new_awards does the same for the post-change set.
CREATE TRIGGER trg_award_batch_audit
AFTER UPDATE ON athletic_awards
REFERENCING OLD TABLE AS old_awards
NEW TABLE AS new_awards
FOR EACH STATEMENT
EXECUTE FUNCTION audit_award_batch();
Per the PostgreSQL documentation, OLD TABLE is available for UPDATE and DELETE events; NEW TABLE is available for INSERT and UPDATE events. A trigger on a DELETE event should declare only OLD TABLE. A trigger on an INSERT event should declare only NEW TABLE. The trigger above is scoped to UPDATE events, where both transition relations are meaningful.
Step 4 — Set the Batch Identifier Before Running
Set a session-level variable to identify this batch in the audit table. The trigger function reads it with current_setting('app.current_batch_id', true).
SET LOCAL app.current_batch_id = 'end_of_season_2026_basketball';
SET LOCAL applies the setting only for the current transaction, ensuring the batch identifier is cleared when the transaction ends — whether by commit or rollback — so it cannot leak into a subsequent session.
Step 5 — Run the Batch Update Inside an Explicit Transaction
Wrap the batch DML in an explicit transaction so the trigger fires with the complete set of changes visible in the transition relations, and so the entire operation can be rolled back based on audit findings.
BEGIN;
SET LOCAL app.current_batch_id = 'end_of_season_2026_basketball';
UPDATE athletic_awards
SET award_title = 'Season MVP',
display_flag = TRUE
WHERE sport = 'Basketball'
AND season_year = 2026
AND award_category = 'player_of_year';
-- Trigger fires here: all affected rows appear in old_awards and new_awards
Step 6 — Review the Audit Table While Still in the Transaction
Before issuing COMMIT, query the audit table to review every change the trigger captured. This review happens inside the open transaction, so changes are visible to the query but have not yet been committed to the production table.
SELECT
audit_id,
operation,
athlete_id,
old_award_title,
new_award_title,
old_sport,
new_sport,
old_season_year,
new_season_year
FROM award_batch_audit
WHERE batch_id = 'end_of_season_2026_basketball'
ORDER BY audit_id;
Check for unexpected sport values, award title mismatches, or season year anomalies across all returned rows. A batch that revised 42 basketball records should return exactly 42 rows in the audit table. A count above or below that signals a scope error that warrants a rollback before any change is committed.
Step 7 — Commit or Roll Back Based on Review Evidence
If the review confirms the batch is correct, update the audit table and commit:
UPDATE award_batch_audit
SET review_status = 'approved',
reviewed_by = 'AD Smith',
review_note = 'All 42 basketball MVP records verified — correct titles and season year'
WHERE batch_id = 'end_of_season_2026_basketball';
COMMIT;
If the review reveals a problem — an unexpected sport value, a wrong season year, or a higher row count than anticipated:
ROLLBACK;
Rolling back removes both the athletic_awards changes and the award_batch_audit rows written by the trigger, because both are part of the same transaction. Any review findings that need to be preserved must be committed through a separate, concurrent database connection before issuing the rollback — PostgreSQL does not natively support autonomous sub-transactions within PL/pgSQL.

Interactive recognition displays depend on verified award records — the transition table audit workflow reviews the entire batch as a set before a single row is committed and the display is updated
Updating the Derived Recognition Count from Transition Table Data
One practical benefit of statement-level transition table triggers is updating a derived recognition count — a summary table tracking how many awards each sport or athlete has received — without scanning the entire athletic_awards table after every batch.
Because NEW TABLE contains only the rows changed in the current batch, a single aggregate query over it gives the delta:
-- Inside the trigger function, after the audit INSERT
INSERT INTO sport_recognition_counts (sport_id, award_count)
SELECT sport_id, COUNT(*) AS batch_count
FROM new_awards
GROUP BY sport_id
ON CONFLICT (sport_id)
DO UPDATE
SET award_count = sport_recognition_counts.award_count
+ EXCLUDED.award_count
- (SELECT COUNT(*) FROM old_awards o
WHERE o.sport_id = EXCLUDED.sport_id);
This computes the net change — new awards added minus old awards replaced — and applies it to the summary table in one statement. The alternative, a full-table COUNT(*) after every batch, grows slower as the award archive grows across seasons and graduating classes. The transition table approach scales with the batch size, not the total row count in the table.
For recognition programs that present award totals on public displays — records boards that count championships, all-conference selections, or athletic department honors by sport — keeping those counts accurate through batch changes is what ensures the numbers families and alumni see are always current.
When FBLA chapters, FFA programs, and other activity-based recognition groups share the same recognition database as athletic programs, the award categories and count logic become more complex. The FBLA and FFA award displays and recognition guide provides context for the display and record requirements those programs bring alongside traditional athletic awards.

Recognition counts and athlete award histories stay accurate through trigger-maintained summary tables — transition table data gives the trigger the batch delta it needs without querying the full award archive
Decision Table: Choosing the Right Trigger Design for Athletic Award Batches
| Scenario | Trigger Type | Transition Tables | Notes |
|---|---|---|---|
| Batch INSERT of new season awards | AFTER INSERT FOR EACH STATEMENT | NEW TABLE only | OLD TABLE not available for INSERT events |
| Batch UPDATE of existing award records | AFTER UPDATE FOR EACH STATEMENT | OLD TABLE and NEW TABLE | Full before/after snapshot for every changed row |
| Batch DELETE of expired or duplicate records | AFTER DELETE FOR EACH STATEMENT | OLD TABLE only | NEW TABLE not available for DELETE events |
| Single-row edits through the recognition platform UI | Row-level trigger | OLD / NEW pseudo-records | Transition tables add overhead with no benefit for single-row changes |
| Enforcing a batch size limit before committing | AFTER ... FOR EACH STATEMENT with RAISE EXCEPTION | NEW TABLE for COUNT | Exception in trigger rolls back the triggering statement entirely |
| Real-time per-row field validation | BEFORE ... FOR EACH ROW | Not available | Row-level BEFORE triggers cannot reference transition relations |
| Audit logging for a display publish gate | AFTER UPDATE FOR EACH STATEMENT | Both | Audit rows written to a separate table before commit decision |
The critical restriction from the PostgreSQL documentation: transition tables are available only in AFTER triggers. A BEFORE trigger or an INSTEAD OF trigger on a view cannot reference transition relations. Any audit logic that requires seeing the complete batch as a whole must use a statement-level AFTER trigger.
A second restriction: OLD TABLE and NEW TABLE are only meaningful for the event types they correspond to. Creating a trigger that fires on INSERT and attempting to query OLD TABLE will produce an empty relation — there are no pre-existing rows to capture for an insert-only operation.
Rollback Evidence: Preserving Review Findings Before Rolling Back
Rolling back a transaction removes the award_batch_audit rows written by the trigger, because those rows are part of the same transaction. If the audit review finds a problem that warrants a rollback, any review findings that need to survive the rollback must be committed through a separate channel — a second database connection, an application-layer log file, or a permanent audit record committed before the rollback call.
A pattern that preserves rollback evidence in an application-layer log:
-- Application code (Python/Node/etc.) before rolling back:
-- 1. Query the audit table for findings while the transaction is still open
-- 2. Write findings to a separate committed store (separate DB connection)
-- 3. Issue ROLLBACK on the original connection
-- Or: log the issue with explicit rollback note via a separate session
-- before closing the transaction
ROLLBACK;
For programs that need a durable rollback record at the database level, a workaround using dblink (connecting to the same database as a second session) allows writing to a permanent log table that survives the rollback of the primary transaction. This adds complexity but provides a fully database-native audit trail regardless of whether the application layer has its own logging.
The rollback evidence record should include the batch ID, the review findings (unexpected values, row count discrepancy, scope errors), the reviewer’s name, the decision rationale, and the timestamp. This creates an accountable record for why a batch was rejected — not just the fact that it was. For award data that feeds recognition ceremonies and athlete history displays, that accountability is part of the broader stewardship of the school’s recognition program.
Schools that produce award ceremony slideshows alongside their recognition database will find that the award ceremony slideshow guide covering what to include before, during, and after a school recognition event depends on the same verified record completeness the transition table audit workflow is designed to protect.
Connecting Transition Table Audits to Display Publication
For school recognition programs, the database layer is the source of truth for what appears on displays. A school’s athletic mission — honoring athletes, teams, alumni, and achievements in a way that inspires the next generation — requires that the records behind those displays are complete and accurate. The kind of values-driven recognition framework described in athletic mission statement examples for schools and awards recognition programs depends on the award records being correct at the source.
Transition table trigger audits provide structured evidence that a batch of changes was reviewed as a complete set before it was committed. When that evidence is tied to a batch_id and a reviewed_by field in the audit table, it creates a reviewable record for every display update. Athletic directors can answer the question “who reviewed this batch, what did they check, and what did they find?” for any update in the audit archive — years after the fact, when a record is questioned or a display is audited.
For recognition programs that display alumni hall of fame profiles, all-conference selections, and season records alongside current-year awards, the credibility of those displays depends on the integrity of the batch processes that write to them. A transition table audit is one mechanism for ensuring that integrity is applied systematically at the point where batch changes are most likely to introduce errors.

Staff reviewing a hall of fame display — transition table audits document the complete review evidence for every batch update so the program can account for every change that reached the display
Platform Integration Considerations
Schools using a cloud-based recognition platform rather than a self-managed PostgreSQL database will find the transition table audit pattern applies at the application layer. The same principles hold: a batch of award changes should be reviewable as a complete set before it is published to displays, the review evidence should be persisted whether or not the batch is approved, and the batch should have a clear rollback path when problems are found before commit.
When evaluating a recognition platform’s batch import and update tooling, ask: does the platform surface the complete change set for review before publishing? Is the review evidence persisted regardless of whether the batch is approved or rejected? Can a batch be rolled back after review without corrupting the live display data? The answers define whether the platform’s import tooling provides the same audit properties as the SQL workflow above.
For programs planning the full ceremony and event workflow around the awards the database manages, the awards ceremony planning guide for hosting memorable recognition events is a practical companion resource for aligning the ceremony program with the verified award records the database confirms as published.
Rocket Alumni Solutions builds award record management and display publishing into a cloud platform where athletic directors can submit award batch updates, review the pending change set before it goes live, and publish only verified records to the recognition display. Request a Rocket Alumni Solutions demo to see how the batch review and publishing workflow operates from award data submission through to display publication.
See Award Batch Review Built Into Your Recognition Platform
Rocket Alumni Solutions gives athletic programs a managed platform where award record batches are reviewed as a complete set before display publication — no partial commits, no unreviewed changes reaching the public display. Request a demo to see how the platform handles batch award updates from submission to verified publication.
Request a DemoFrequently Asked Questions
What is a transition table in a PostgreSQL trigger?
A transition table is a named, read-only relation that PostgreSQL makes available inside a statement-level AFTER trigger function. It contains the complete set of rows affected by the triggering statement — either as they existed before the change (OLD TABLE) or as they will exist after the change (NEW TABLE). The trigger function can query these relations using standard SQL, including joins, aggregates, and filters, giving it access to the full batch of affected rows rather than a single row at a time. Transition tables are declared with the REFERENCING clause in the CREATE TRIGGER statement, as documented in the PostgreSQL CREATE TRIGGER reference.
When can OLD TABLE and NEW TABLE be used in PostgreSQL triggers?
OLD TABLE is available for AFTER triggers on UPDATE and DELETE events. It contains every row as it existed before the triggering statement ran. NEW TABLE is available for AFTER triggers on INSERT and UPDATE events. It contains every row as it will exist after the triggering statement commits. Neither OLD TABLE nor NEW TABLE is available in BEFORE triggers, INSTEAD OF triggers, or row-level triggers. Both are only available in statement-level (FOR EACH STATEMENT) AFTER triggers. Attempting to use OLD TABLE in an INSERT trigger or NEW TABLE in a DELETE trigger is not valid per the PostgreSQL documentation.
How does a transition table trigger differ from a row-level trigger for batch operations?
A row-level trigger fires once for each row affected by the triggering statement. For a batch UPDATE of 80 award records, a row-level trigger fires 80 times, with access only to the single current row via the OLD and NEW pseudo-records. A statement-level trigger with transition tables fires once for the entire statement, with access to all 80 affected rows simultaneously as queryable relations. This makes statement-level triggers the correct choice for batch audit logic that needs to examine the full change set, enforce batch-level size limits, or update a summary count using only the delta from the batch rather than scanning the full awards table.
Can a trigger function roll back a batch based on transition table contents?
Yes. A trigger function that raises an exception with RAISE EXCEPTION aborts the current transaction, which includes the DML statement that fired the trigger. Because the trigger runs inside the same transaction as the triggering statement, the exception rolls back every change in that transaction — including all rows visible in OLD TABLE and NEW TABLE. This makes it possible to enforce batch-level rules such as a maximum row count or a required review gate and abort the transaction automatically when the rule is violated, before any change reaches a committed state. Audit rows written to the audit table are also rolled back, so findings that need to be preserved must be committed through a separate database connection before rolling back.
How do transition tables help update a recognition count after a batch award change?
Inside a statement-level trigger function, NEW TABLE contains only the rows affected by the current batch — not the entire awards table. An aggregate query over NEW TABLE gives the count of new or updated awards by sport or athlete for this batch alone. Combined with a count from OLD TABLE for the rows that were replaced, the trigger computes the net delta and applies it to a recognition count summary table in one statement. This is more efficient than running a full-table COUNT after every batch because the query cost scales with the batch size rather than the total number of award records accumulated over the program's history.
Conclusion: Batch Review as a Commitment to Complete Recognition
An athletic awards database transition table trigger audit turns a batch update from an all-or-nothing operation into a reviewable checkpoint. Statement-level AFTER triggers with REFERENCING OLD TABLE and NEW TABLE give the trigger function access to the complete before-and-after state of every row the batch touched, in queryable form, before the transaction commits. That access makes it possible to audit the full change set, update a derived recognition count from the batch delta, and either commit a confirmed-correct batch or roll back a problematic one — with the evidence of what was reviewed persisted in the audit table.
For athletic programs whose mission is to honor every athlete, team, alumni member, and achievement completely and accurately, the database processes behind their recognition displays deserve the same care and accountability as the displays themselves. A transition table trigger audit is one mechanism for ensuring that care is applied systematically — at the point where batch changes could otherwise reach a display without a structured review that any staff member or administrator can examine later.

A school wall of honor and digital display represents years of verified award records — transition table trigger audits provide the review checkpoint that confirms each batch is correct before it reaches the public display
































