One of the most important concepts in Oracle Fusion SQL is effective dating. Many Oracle Fusion tables do not store only the current version of a record. Instead, they store the full history of changes over time - meaning a single employee, assignment, department, salary, or job can exist in multiple versions across different date ranges.
If your SQL does not handle effective dates correctly, the results are usually wrong:
- Duplicate rows
- Inflated headcount numbers
- Incorrect joins
- Historical data mixed with current data
- Broken aggregations
- Unexpected Cartesian-style row multiplication
This is one of the most common causes of reporting problems in OTBI validations, BI Publisher data models, and Oracle Fusion analytics projects. This guide explains how Oracle Fusion effective dating works, the query patterns used in real-world reporting, and the most common mistakes developers make.
What Are Effective-Dated Tables?
In Oracle Fusion, tables ending with _F or _M are usually effective-dated. Instead of updating a row in place, Oracle creates a new version of the row whenever something changes.
| PERSON_ID | JOB | EFFECTIVE_START_DATE | EFFECTIVE_END_DATE |
|---|---|---|---|
| 1001 | Analyst | 2023-01-01 | 2024-05-31 |
| 1001 | Senior Analyst | 2024-06-01 | 4712-12-31 |
The employee was an Analyst until May 31, 2024, then became a Senior Analyst beginning June 1, 2024. Oracle keeps both rows to preserve history.
Common Effective-Dated Tables in Oracle Fusion
| PER_ALL_PEOPLE_F | Core worker records |
| PER_PERSON_NAMES_F | Employee names |
| PER_ALL_ASSIGNMENTS_M | Assignments |
| PER_JOBS_F | Jobs |
| PER_GRADES_F | Grades |
| PER_POSITIONS_F | Positions |
| HR_LOCATIONS_ALL_F | Locations |
| HR_ALL_ORGANIZATION_UNITS_F | Departments |
Some Finance tables are transactional rather than effective-dated, but configuration and HR-linked structures often are.
Supplier and procurement operational tables are usually transactional, while organization and HR-linked structures may be effective-dated.
Understanding the Sentinel Date: 31-DEC-4712
Oracle Fusion commonly uses 31-DEC-4712 as the open-ended date, meaning: this version is currently active and has no defined end date. This is usually the current row.
| EFFECTIVE_START_DATE | EFFECTIVE_END_DATE | Meaning |
|---|---|---|
| 2024-06-01 | 4712-12-31 | Currently active - open-ended record |
Not every module behaves identically. Some tables contain future-dated changes, and some implementations use additional logic. Because of this, the safest production pattern is still TRUNC(SYSDATE) BETWEEN effective_start_date AND effective_end_date rather than relying only on the sentinel value.
Why Effective-Date Logic Matters
Without proper date filtering, a single worker with 4 department changes, 3 salary changes, and 2 job changes can silently become 4 × 3 × 2 = 24 rows from a single employee if joins are not filtered correctly. This causes:
- One employee appearing 5–20 times
- Salary joins multiplying rows
- Inaccurate aggregations
- Very slow BI Publisher reports
- Unreliable headcount numbers
Five Essential Effective-Date Query Patterns
Current Active Records
The most common Oracle Fusion SQL pattern. Retrieve only the version active today. Every effective-dated table has its own independent filter - this prevents historical rows from multiplying the result set.
SELECT
p.person_id,
n.display_name,
a.assignment_number,
a.assignment_status_type
FROM
per_all_people_f p
JOIN per_person_names_f n
ON n.person_id = p.person_id
AND n.name_type = 'GLOBAL'
AND TRUNC(SYSDATE) BETWEEN n.effective_start_date AND n.effective_end_date
JOIN per_all_assignments_m a
ON a.person_id = p.person_id
AND a.primary_flag = 'Y'
AND TRUNC(SYSDATE) BETWEEN a.effective_start_date AND a.effective_end_date
WHERE
TRUNC(SYSDATE) BETWEEN p.effective_start_date AND p.effective_end_date
Point-in-Time Snapshot
Retrieve records exactly as they existed on a historical date. Useful for audit reporting, historical headcount, compensation analysis, and compliance reviews. Use the same date parameter consistently across all joined tables - mixing different dates creates logically inconsistent results.
SELECT
p.person_number,
n.display_name,
a.assignment_number,
j.name AS job_name
FROM
per_all_people_f p
JOIN per_person_names_f n
ON n.person_id = p.person_id
AND n.name_type = 'GLOBAL'
AND :p_as_of_date BETWEEN n.effective_start_date AND n.effective_end_date
JOIN per_all_assignments_m a
ON a.person_id = p.person_id
AND a.primary_flag = 'Y'
AND :p_as_of_date BETWEEN a.effective_start_date AND a.effective_end_date
JOIN per_jobs_f j
ON j.job_id = a.job_id
AND :p_as_of_date BETWEEN j.effective_start_date AND j.effective_end_date
WHERE
:p_as_of_date BETWEEN p.effective_start_date AND p.effective_end_date
Detecting Changes During a Period
Identify when a new row version became effective. Useful for promotion tracking, department transfers, salary changes, and workforce movement reports.
SELECT
a.person_id,
a.assignment_number,
a.effective_start_date AS change_date,
a.job_id,
a.location_id,
a.organization_id
FROM
per_all_assignments_m a
WHERE
a.primary_flag = 'Y'
AND a.effective_start_date BETWEEN :p_from_date AND :p_to_date
ORDER BY
a.effective_start_date DESC
Full Historical Timeline
Retrieve every version of a record. Useful for troubleshooting, audit analysis, HR investigations, and understanding lifecycle changes. Remove date filters intentionally, always order chronologically, and expect multiple rows per person.
SELECT
a.person_id,
a.assignment_number,
a.effective_start_date,
a.effective_end_date,
a.job_id,
a.organization_id,
a.location_id,
a.assignment_status_type
FROM
per_all_assignments_m a
WHERE
a.person_id = :p_person_id
AND a.primary_flag = 'Y'
ORDER BY
a.effective_start_date
Open Records Using the Sentinel Date
A shorthand pattern for active rows. Works only if the table consistently uses the sentinel, future-dated rows are not involved, and your implementation follows standard conventions. The safer production approach is still TRUNC(SYSDATE) BETWEEN effective_start_date AND effective_end_date.
SELECT
p.person_id,
n.display_name
FROM
per_all_people_f p
JOIN per_person_names_f n
ON n.person_id = p.person_id
AND n.name_type = 'GLOBAL'
WHERE
p.effective_end_date = DATE '4712-12-31'
AND n.effective_end_date = DATE '4712-12-31'
Effective-Date Logic in Real Oracle Fusion Reporting
Four Common Effective-Date Mistakes
_F or _M table without any date condition returns every historical row. Always filter effective-dated tables unless you intentionally need history.-- Returns ALL historical versions SELECT * FROM per_all_assignments_m
PER_ALL_PEOPLE_F but not the joined _M tables still allows historical rows from those joined tables to multiply the result set. Every effective-dated table needs its own filter.SYSDATE includes a time component. Many effective date columns are stored as midnight values. Using bare SYSDATE in a BETWEEN condition can unexpectedly miss today's row. Always use TRUNC(SYSDATE).primary_flag = 'Y' and name_type = 'GLOBAL' where applicable.Recommended Best Practices
_F and _M table in the join chain needs its own date condition.p = people, n = person names, a = assignments, j = jobs, org = organizations.:p_as_of_date consistently instead of mixing SYSDATE across tables.Quick Reference: Effective-Date Filter Patterns
| Use Case | Recommended Filter |
|---|---|
| Current rows | TRUNC(SYSDATE) BETWEEN effective_start_date AND effective_end_date |
| Historical snapshot | :p_date BETWEEN effective_start_date AND effective_end_date |
| Detect changes | effective_start_date BETWEEN :from_date AND :to_date |
| Full history | (no date filter) ORDER BY effective_start_date |
| Open rows only | effective_end_date = DATE '4712-12-31' |
Key Takeaways
Effective-date handling is one of the foundational skills of Oracle Fusion SQL. Most duplicate rows, incorrect headcounts, broken joins, and slow BI Publisher reports ultimately trace back to incorrect handling of _F and _M tables.
Understanding how effective-dated tables work, when to use current versus historical logic, how to synchronize date filters across joins, and how to avoid row multiplication is essential for writing reliable Oracle Fusion SQL.
Build Correct Effective-Date SQL Faster with FusionLens SQL
Browse Oracle Fusion effective-dated tables with column descriptions and data types - understand _F and _M table structures before writing date-filtered SQL.
Autocomplete that knows Oracle Fusion table names and columns — including _F and _M tables — so you get effective-date filters right from the start.
Compare effective-date query iterations side by side - track which date filter changes corrected duplicate rows and improved headcount accuracy.