Last active
January 26, 2025 21:16
-
-
Save ingoogni/6601119e3cd82e90389e4efda05d3db5 to your computer and use it in GitHub Desktop.
Table that keeps its own history, SQLite
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Database schema SQLite for a table that keeps its own history and | |
never deletes data. | |
On update, historic data is kept under a new id and references the original | |
creation id. The end of life timestamp (ts_eol) is set so a time line can be | |
built. The new data is used to update the data under the existing id. | |
On delete, nothing is deleted. Only the ts_eol timestamp is set to current. | |
*/ | |
DROP TABLE IF EXISTS Trigger; | |
DROP TABLE IF EXISTS Keep; | |
DROP VIEW IF EXISTS view_Keep; | |
/* | |
The table that keeps its own history. ts_eol and hist_id are used to keep history plus | |
one column with a partial index. | |
*/ | |
CREATE TABLE IF NOT EXISTS Keep( | |
id INTEGER PRIMARY KEY NOT NULL, | |
-- start data block | |
person TEXT NOT NULL, -- part of partial index pidx_person_eol | |
asset TEXT NOT NULL, | |
ts_from TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, -- is not required for keeping history | |
-- .... | |
-- .... | |
-- end datablock | |
ts_eol TEXT DEFAULT NULL, | |
hist_id INTEGER DEFAULT NULL REFERENCES Keep(id), | |
CONSTRAINT id_not_historic CHECK(id <> hist_id) | |
); | |
CREATE UNIQUE INDEX pidx_person_eol | |
ON Keep(person) | |
WHERE ts_eol IS NULL | |
; | |
--CREATE INDEX historic_idx | |
-- ON Keep(hist_id) | |
--; | |
/* | |
the view to run the updates and deletes against | |
*/ | |
CREATE VIEW IF NOT EXISTS view_Keep | |
AS | |
SELECT id, person, asset, ts_from,ts_eol, hist_id -- * | |
FROM Keep | |
WHERE ts_eol IS NULL | |
; | |
-- the below has to be created on (writer) connection to the db. | |
/* | |
The temp table holds a flag that makes it possible to directy update the Keep table. | |
When FALSE direct updates are not possible. | |
*/ | |
CREATE TEMP TABLE IF NOT EXISTS Trigger( | |
trigger_on TEXT NOT NULL UNIQUE, | |
state BOOL CHECK(state IN (FALSE, TRUE)) | |
); | |
INSERT INTO Trigger(trigger_on, state) | |
VALUES ('insert_keep', FALSE) | |
; | |
/* | |
trigger to prevent updates directly to the Keep table | |
*/ | |
CREATE TEMP TRIGGER IF NOT EXISTS before_update_Keep | |
BEFORE UPDATE | |
ON main.Keep | |
WHEN ( | |
SELECT state | |
FROM Trigger | |
WHERE trigger_on = 'insert_keep' | |
) = FALSE | |
BEGIN | |
SELECT RAISE(FAIL,'ERROR! Cannot update directly to Keep. Update to view_Keep'); | |
END | |
; | |
/* | |
trigger to update the Keep table. The insert_keep flag has to be set | |
to TRUE before the update or else the before_update_Keep trigger will kick in. | |
*/ | |
CREATE TEMP TRIGGER IF NOT EXISTS update_view_Keep | |
INSTEAD OF UPDATE | |
ON view_Keep | |
BEGIN | |
UPDATE Trigger | |
SET state = TRUE | |
WHERE trigger_on = 'insert_keep' | |
; | |
UPDATE Keep | |
SET asset = new.asset, ts_from = CURRENT_TIMESTAMP | |
WHERE person = old.person AND old.ts_eol IS NULL | |
; | |
UPDATE Trigger | |
SET state = FALSE | |
WHERE trigger_on = 'insert_keep' | |
; | |
INSERT INTO Keep (person, asset, hist_id, ts_from, ts_eol) | |
SELECT old.person, old.asset, old.id, old.ts_from, CURRENT_TIMESTAMP | |
; | |
END | |
; | |
/* | |
trigger to prevent deletes directly to the Keep table | |
*/ | |
CREATE TEMP TRIGGER IF NOT EXISTS before_delete_Keep | |
BEFORE DELETE | |
ON main.Keep | |
BEGIN | |
SELECT RAISE(FAIL,'ERROR! Cannot delete directly from Keep. Delete from view_Keep'); | |
END | |
; | |
/* | |
trigger to "delete" from the Keep table. The insert_keep flag has to be set | |
to TRUE before the update or else the before_update_Keep trigger will kick in. | |
*/ | |
CREATE TEMP TRIGGER IF NOT EXISTS delete_view_Keep | |
INSTEAD OF DELETE | |
ON view_Keep | |
BEGIN | |
UPDATE Trigger | |
SET state = TRUE | |
WHERE trigger_on = 'insert_keep' | |
; | |
UPDATE Keep | |
SET ts_eol = CURRENT_TIMESTAMP | |
WHERE id = old.id | |
AND ts_eol IS NULL | |
AND hist_id IS NULL | |
; | |
UPDATE Trigger | |
SET state = FALSE | |
WHERE trigger_on = 'insert_keep' | |
; | |
END | |
; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Al die willen te kaap'ren varen | |
Moeten mannen met baarden zijn | |
Jan, Pier, Tjores en Corneel | |
Die hebben baarden, die hebben baarden | |
Jan, Pier, Tjores en Corneel | |
Die hebben baarden, zij varen mee | |
*/ | |
-- writer queries | |
INSERT INTO Keep(person, asset) | |
VALUES ('jan', 'baard'), ('pier', 'baard'), ('tjoris', 'baard'), ('corneel', 'baard') | |
; | |
UPDATE view_Keep | |
SET asset = 'glad' | |
WHERE person = 'corneel' | |
; | |
UPDATE view_Keep | |
SET asset = 'bakkebaard' | |
WHERE person = 'jan' | |
; | |
UPDATE view_Keep | |
SET asset = 'glad' | |
WHERE person = 'pier' | |
; | |
UPDATE view_Keep | |
SET asset = 'soul patch' | |
WHERE person = 'corneel' | |
; | |
DELETE FROM view_Keep | |
WHERE person = 'corneel' | |
; | |
-- transaction to reinstate a deleted person | |
BEGIN TRANSACTION | |
INSERT INTO Keep (person, asset, hist_id, ts_from, ts_eol) | |
SELECT person, asset, id, ts_from, CURRENT_TIMESTAMP | |
FROM KEEP | |
WHERE person = 'corneel' | |
AND ts_eol IS NOT NULL | |
AND hist_id IS NULL | |
; | |
UPDATE Trigger | |
SET state = TRUE | |
WHERE trigger_on = 'insert_keep' | |
; | |
UPDATE Keep | |
SET ts_eol = NULL, ts_from = CURRENT_TIMESTAMP | |
WHERE person = 'corneel' | |
AND ts_eol IS NOT NULL | |
AND hist_id IS NULL | |
; | |
UPDATE Trigger | |
SET state = FALSE | |
WHERE trigger_on = 'insert_keep' | |
; | |
ROLLBACK; | |
-- Reader queries | |
SELECT * | |
FROM Keep | |
WHERE person = 'corneel' | |
-- AND hist_id IS NOT NULL | |
; | |
/*history length*/ | |
SELECT person, count(id) | |
FROM Keep | |
WHERE person = 'corneel' | |
; | |
/*history since*/ | |
SELECT person, count(id), min(ts_from) AS since | |
FROM Keep | |
WHERE person = 'corneel' | |
; | |
/*sql to get deleted id's*/ | |
SELECT id, person, asset, ts_from, ts_eol | |
FROM Keep | |
-- a deleted item / item timeline, has a set ts_eol, but no hist_id. | |
WHERE ts_eol IS NOT NULL | |
AND hist_id IS NULL | |
; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment