How to keep an unique counter per row with PostgreSQL?
SEQUENCE is guaranteed to be unique, and your use-case looks applicable if your number of documents isn't too high (else you have a lot of sequences to manage). Use the RETURNING clause to get the value that was generated by the sequence. For example, using 'A36' as a document_id:
- Per document, you could create a sequence to track the increment.
Managing the sequences will need to be handled with some care. You could perhaps keep a separate table containing the document names and the sequence associated with that
document_id
to reference when inserting/updating thedocument_revisions
table.CREATE SEQUENCE d_r_document_a36_seq; INSERT INTO document_revisions (document_id, rev) VALUES ('A36',nextval('d_r_document_a36_seq')) RETURNING rev;
This is often solved with optimistic locking:
SELECT version, x FROM foo;
version | foo
123 | ..
UPDATE foo SET x=?, version=124 WHERE version=123
If the update returns 0 rows updated, you've missed your update because someone else already update the row.
Assuming you store all revisions of the document in a table, an approach would be to not store the revision number but calculate it based on the number of revisions stored in the table.
It is, essentially, a derived value, not something that you need to store.
A window function can be used to calculate the revision number, something like
row_number() over (partition by document_id order by <change_date>)
and you'll need a column something like change_date
to keep track of the order of the revisions.
On the other hand, if you just have revision
as a property of the document and it indicates "how many times the document has changed", then I would go for the optimistic locking approach, something like:
update documents
set revision = revision + 1
where document_id = <id> and revision = <old_revision>;
If this updates 0 rows, then there has been intermediate update and you need to inform the user of this.
In general, try to keep your solution as simple as possible. In this case by
- avoiding the use of explicit locking functions unless absolutely necessary
- having fewer database objects (no per document sequences) and storing fewer attributes (don't store the revision if it can be calculated)
- using a single
update
statement rather than aselect
followed by aninsert
orupdate