3 ms·
While I appreciate all criticism of this approach in the comments, it's not that simple. 1. There may be audit policies, compliance policies, retention policie
by adontz 2y ago
While I appreciate all criticism of this approach in the comments, it's not that simple.
1. There may be audit policies, compliance policies, retention policies in place. You may HAVE to keep data which is deleted from user's perspective.
2. People make mistakes. While I agree that restoring a long ago soft-deleted object may be very tricky because of referential integrity. It's easy to restore a recently (hours, days) soft-deleted object. Not only easy, it's much faster than restoring a point-in-time backup of an entire database. Also restored version is guaranteed to be the latest one, unlike in backup.
3. PostgreSQL supports partial indexes and partial unique constraints. https://www.postgresql.org/docs/current/indexes-partial.html https://www.postgresql.org/docs/current/indexes-partial.html
Other databases support similar features too. That allows excluding soft-deleted records from indexes, makes indexes smaller. A good query does not scan a table anyway. If one needs to read less pages from a table, PostgreSQL has CLUSTER command. If there are too many soft-deleted tuples, maybe it's time to think about an archive table anyway.
Unique constraints may ignore soft-deleted objects, so only active objects need to be unique. That is very useful if you need to support idempotent calls. Additionally that may actually be a security feature too. If I create a new user with the same username as an old deleted user, I know that username is unique, but also, that I will not accidentally get permissions of the old user assigned.
4. The worst is tooling. Forgetting "WHERE deleted_at IS NOT NULL" or "WHERE NOT is_deleted" is a very real problem. I would rather advise against soft deleted objects because of that problem alone.
On the other hand, if you use a decent ORM, (and why would not you?) like SQLAlchemy, Django ORM or EntityFramework/Linq (sorry for guys who are not Python/.Net, I do not have a good example for you), then it's quite easy to create a default model query which is not empty. But honestly, if your system is complex enough and has any custom row level security, if it is multi-tenant, if not all users see everything, you already have quite similar problem, you already have to start with non-empty query which already has WHERE and possibly even JOIN clauses. Without a good ORM it will suck anyway.
- b-man 2y agoJust use temporal tables. It would cover all the cases you brought up.
- adontz 2y agoThis thing? https://learn.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables?view=sql-server-ver16 https://learn.microsoft.com/en-us/sql/relational-databases/t... Are not we talking about PostgreSQL?
- b-man 2y agoTemporal tables are an implementation of one of SQL 2011's main features: system time. https://sigmodrecord.org/publications/sigmodRecord/1209/pdfs/07.industry.kulkarni.pdf https://sigmodrecord.org/publications/sigmodRecord/1209/pdfs... Postgres itself has not yet added such to the core, since it moves about as fast as an elephant. There are extensions that do implement it (https://wiki.postgresql.org/wiki/Temporal_Extensions https://wiki.postgresql.org/wiki/Temporal_Extensions).
- adontz 2y agoHelp me here please. I really do not see how WHERE CURRENT_TIMESTAMP BETWEEN start AND end is much better than WHERE deleted_at IS NOT NULL Also, if we are talking about real use case of audit, not a simplified artificial one, my real tables were more like changeset(id INT, timestamp DATETIME, user ^USER, host TEXT, ip_address TEXT, <other security data>) datatable1(id INT, created_at ^changeset, updated_at ^changeset, deleted_at ^changeset NULL, <other fields>) datatable2(id INT, created_at ^changeset, updated_at ^changeset, deleted_at ^changeset NULL, <other fields>) datatable3(id INT, created_at ^changeset, updated_at ^changeset, deleted_at ^changeset NULL, <other fields>)
- b-man 2y agoIt is better, as others have written, because it preserves integrity constraints.