There's always an events table (2022)
brandur.org
brandur.org
Nah, I am quite sure
for item in conn.query('SELECT * FROM event'):
if item.created_at < created_at_horizon:
conn.exec('DELETE FROM event WHERE ' + ' AND '.join(f'{k} = :{k}' for k in item.__dict__.keys), item.__dict__)
is fine, so I won't even bother measuring it. It's also secure, don't worry: as you can see I used prepared statements in it.(Facts are immutable, values change with time: https://www.youtube.com/watch?v=D6nYfttnVco )
I would much rather a solution where I can offload old events into some other storage system, so that it doesn't clog my database but the information is still retrievable.
Don't collect data if you don't have a clear purpose for it, IMO.
At large enough scale, it becomes mandatory to separate the event bus from the system of record. I witnessed this in semiconductor manufacturing. There was a gigantic transaction table of finalized things, and all of the detail event data was on a separate messaging bus/system with its own log files. The raw event data was on the order of 300-400 megabytes per hour per mfg line.
Even with this separation, the high level table was still very scary to use. An unconstrained "SELECT *" would invariably result in a visit from the on-call DBA.
I was going to post about this very topic, but you accurately pointed it out.