
We were working with a customer operating in the energy and utilities space, where large volumes of metering and device data are continuously collected and processed.
Their platform ingests data from:
Smart meters and field devices
Grid and energy consumption systems
Upstream applications generating usage and telemetry events
This data typically flows through ingestion layers and message queues before being persisted into Oracle for downstream processing, things like aggregation, validation, and billing.
As expected, this resulted in high-volume, append-heavy tables, with new data arriving continuously throughout the day.
Initially, the system handled this well. But as the data footprint grew, a few challenges started surfacing:
Queries, especially those combining recent and historical data, started slowing down
Retention-based cleanup jobs became increasingly heavy
Maintenance activities began taking longer than expected
Even routine operations on these tables started carrying some risk
Since the data was inherently time-based, partitioning would have been the natural solution here.
However, the database was running on Oracle SE2, which meant native partitioning wasn't available.
Upgrading to Enterprise Edition was considered, but given the cost implications and the fact that this was a very specific requirement, it wasn't the preferred path.
So instead of forcing a change in licensing or redesigning the entire data flow, we focused on solving the problem within the existing setup, by rethinking how the data was organized inside Oracle.

Splitting one large table into monthly child tables
Instead of trying to optimize one large table, we changed the way data was laid out.
We split it into smaller tables:
sql
USAGE_DATA_202501
USAGE_DATA_202502
USAGE_DATA_202503
...
Each table holds a slice of data (monthly in our case).
On top of that, we exposed everything through a single view, so applications didn't need to change.
That part is straightforward.
The real challenge was: how do we make this sustainable without turning it into a manual maintenance problem?
We created a package:
sql
TESL_DLM_MANAGE_PARTITIONS
The goal wasn't to replicate Oracle partitioning exactly, but to build something that:
Works reliably in production
Requires minimal manual effort
Doesn't break existing integrations

This is where most similar approaches fail, not in the idea, but in the execution.
We focused heavily on the operational gaps.
The core procedure (MAIN_PROCESS) handles:
Identifying existing partitions
Figuring out what new partitions are needed
Pre-creating future partitions
Keeping metadata in sync
Once configured, there's no need to manually create tables every month or day.

We avoided hardcoding logic.
All behavior is controlled using:
TESL_DLM_PARTITION_META_DATA
TESL_DLM_PARTITION_TABLE_DATA
This allows:
Managing multiple tables through the same framework
Supporting different partition strategies (Yearly / Monthly / Weekly / Daily)
Controlling retention cleanly
Instead of redefining structures manually, we used:
sql
DBMS_METADATA.GET_DDL
Then dynamically modified:
Table name
Constraint names
Index names
One important learning here: constraint and index names must be suffixed per partition, otherwise you run into conflicts immediately.
Whenever a new partition table is created:
Existing privileges are fetched
Reapplied dynamically
Handled via:
GET_OBJECT_PRIVILEGES
GRANT_OBJECT_PRIVILEGES
This ensures access control remains consistent without manual effort.
Before recreating objects, we:
Capture existing synonyms
Recreate them afterward
Handled through:
GET_SYNONYMS
CREATE_SYNONYMS
The view is rebuilt dynamically using UNION ALL whenever partitions change.
But before doing that, we:
Capture privileges
Capture synonyms
Recreate the view
Restore everything
Recreate triggers
This is handled in RECREATE_VIEW.
From the application perspective, nothing changes.
A UNION ALL view alone is not enough.
To make it behave like a single table, we added INSTEAD OF triggers:
INSERT routed to the correct partition
DELETE executed on the correct partition
UPDATE handled within the correct partition
Routing is based on the partition column (typically a date).
But that wasn't the only consideration.
In the original setup, the base tables already had BEFORE and AFTER triggers handling things like validations, audit columns, or downstream processing.
So while introducing this routing layer, we ensured that:
Data ultimately lands in the actual partition tables
Existing BEFORE/AFTER INSERT, UPDATE, and DELETE triggers continue to fire as expected
No application-side logic had to be rewritten
This was important because it allowed us to introduce partitioning without disrupting existing business logic tied to those triggers.
In effect, the view plus INSTEAD OF triggers act as a routing layer, while the underlying tables continue to behave exactly as they did before.

We created a fallback table:
sql
<MAIN_TABLE>_DEFAULT
If a partition doesn't exist, or data doesn't match the expected range, it gets stored here instead of failing.
We also handled cleanup as part of the framework:
Old partitions are marked based on retention policy
Optionally dropped
Handled using:
DISABLE_OLD_PARTITIONS
DROP_OLD_PARTITIONS
This avoids heavy DELETE operations and keeps things predictable.
We added logging using an autonomous transaction:
Debug logs (optional)
Error logs (always captured)
Handled via WRITE_TO_LOG.
This made troubleshooting much easier in production.
Everything starts with:
sql
SETUP_PARTITIONS
This:
Registers the table
Creates default and initial partitions
Builds the view
Creates triggers
Starts automation
After that, the system largely runs on its own.
No dependency on Enterprise Edition
Fully automated after setup
Easy to onboard new tables
Efficient data cleanup (drop vs delete)
Minimal application changes
No optimizer-level partition pruning
UNION ALL view grows over time
Trigger-based routing adds some overhead
Requires disciplined setup
So yes, it's not a replacement for native partitioning.
This started as a workaround, but it turned into a fairly robust framework.
It doesn't replicate Oracle partitioning exactly, but it solves a large part of the problem in a practical and cost-effective way.
If you're on Oracle Standard Edition and dealing with growing, time-based datasets, this approach is worth considering, especially if you invest in:
Automation
Metadata-driven design
Proper handling of edge cases
Because in the end, the biggest win here wasn't just performance, it was operational simplicity.