This has become a Ruby gem: https://github.com/ifad/chronomodel
A temporal database system on PostgreSQL using table inheritance and the rule system.
This is a data structure for a Slowly-Changing Dimension Type 2 temporal database, implemented using only PostgreSQL >= 9.0 features.
Any application code is completely unaware of the temporal features: queries are done against a view that behaves exactly like a plain table (it can be SELECTed, UPDATEd, INSERTed INTO and DELETEd FROM), but behind the scenes the database redirects the queries to backend tables holding actual data, using the PostgreSQL rule system.
All data is stored both in a current table and in an history one, inheriting from the current. To query historical data at a given date, a where date between valid_from and valid_to
clause is enough.
The current and history tables are created in different schemas, while the view is in the default public schema: the application will see only the view by default. By changing the schema search path it is possible to redirect queries to the history tables without changing the application code, only by adding the aforementioned WHERE clause. Moreover, this allows to do JOINs between history tables and non-temporal ones.
Caveat: tre rules must be kept in sync with the schema, and updated if it changes.
If your ORM / Framework supports database migrations, and your language supports monkeypatching or inheritance, this means that you'll have to override its migration DSL to do tables and views creation and rules housekeeping.
E.g. for Ruby on Rails, add_column
and friends could be hooked to look whether a table is temporal and update the rules as well, while create_table
and friends can create the schemas and current and history tables e.g. by defining a new option e.g. :temporal => true
.
In the
WHERE
clause of theUPDATE
statement in thecountries_upd
andcountries_del
RULEs you are using two fields:"id"
column is indexed by the"country_id" INDEX
, so the index is used for searching, but"valid_to"
column can only be used as a filter predicate instead of an access predicate because thetimestamps INDEX
starts with the"valid_from"
column that does not appear in theWHERE
section.In case you have many rows in the
"history.countries" TABLE
sharing the same"id"
value:("id", "valid_to")
index could speed up the searching;"valid_from"
column may help.