Reputation: 8478
My primary goal with this is to make implementing revision histories and journaling easier.
I found myself wondering if it's possible, using Flask-SQLAlchemy (or just straight up SQL), to get an auto incremented non-unique integer for mysql. I found this stack overflow post which is close to what I want to do but the question is focused on a primary key. For example, if my table had these columns,
revision_id = db.Column(db.Integer, nullable=False)
post_id = db.Column(db.Integer, nullable=False)
__table_args__ = (
PrimaryKeyConstraint('post_id', 'revision_id'),
)
Would it be possible to create a new post that has a revision_id of 1 and a post_id of max(post_id) + 1 without the problem of two users attempting to create a post at the same time and creating the same post_id?
The advantage of this system is that it makes post history (and differencing) very simple. Whenever someone wants to modify a post I'd use the same post_id as the original and increment the revision_id (which, now that I'm typing this out, has the same problem).
Update:
Sylvain Leroux put me on the right track for how to solve my problem. I needed to set both tables as a primary key in sqlalchemy. If there's more then one primary key in sqlalchemy it doesn't assume that they're unique. Here's my current definition,
revision_id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=False, default=1)
post_id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
__table_args__ = (
PrimaryKeyConstraint('post_id', 'revision_id'),
)
Which produces this SQL
CREATE TABLE `post` (
revision_id INTEGER NOT NULL,
post_id INTEGER NOT NULL AUTO_INCREMENT,
PRIMARY KEY (post_id, revision_id),
)
which allowed me to insert with and without a post_id.
All credit to Sylvain Leroux since I merely translated his answer to SQLAlchemy.
Upvotes: 2
Views: 2681
Reputation: 51980
To quote a classic MySQL error message:
there can be only one auto column and it must be defined as a key:
auto_increment
column must be either (in) the primary key or (in) a key (also known as index in MySQL, which may or may be unique).
As for:
SELECT MAX(id) FROM tbl INTO @new_id;
INSERT INTO tbl (id, ...) VALUES (@new_id, ....);
You clearly understand that if two concurrent requests do the same thing, you will end up having two new rows, totally unrelated, with the same ID. An you probably don't want to use table locks to avoid that pitfall.
As of myself, I would say "don't do that". Maybe that would make some things more easy in your application, but I bet that would made tons of other things far more complicated or much less robust.
But ... if the real problem if to keep the "post id" constant, remember that auto_increment could be part of a key (http://sqlfiddle.com/#!2/9b4b45/1):
create table tbl(id int auto_increment not null,
rev int not null default 1,
content TEXT,
primary key(id,rev));
-- insert 3 new contents with "auto id" and default revision 1
insert into tbl (content) values ("alpha"), ("bta"), ("gamma");
Say there is an error in the item id2,rev1. You could insert a new revision:
insert into tbl (id, rev, content) values (2,2,"beta");
If you want to see the various revision of item id2:
select * from tbl where id=2 order by rev;
You will have to find how to do that with SQLAlchemy (:D), but this is definitively possible with MySQL.
Upvotes: 4