Reputation: 718
I have a table with primary key (its name is "id") defined as auto_increment. I use NULL in INSERT statements to "fill" the id value. It works, of course. However now I need to "move" an existing record to a new primary key value (the next available, the value is not so much important, but it must be a new one, and the last one if ordered by id). How can I do it in an "elegant" way? Since the "use NULL at INSERT" does not work too much with UPDATE:
update idtest set id=NULL where id=1;
This simply makes the id of the record zero. I would expect to do the same thing as with INSERT, but it seems my idea was incorrect.
Of course I can use "INSERT ... SELECT" statement, then a DELETE on the old one, or I can use something like MAX(id) + 1 to UPDATE the id of the old record in one step, etc, but I am curious if there is a finer solution.
Also, the MAX(id) solution doesn't seem to work either by the way:
mysql> update idtest set id=max(id)+1 where id=3;
ERROR 1111 (HY000): Invalid use of group function
mysql> update idtest set id=(select max(id)+1 from idtest) where id=3;
ERROR 1093 (HY000): You can't specify target table 'idtest' for update in FROM clause
Upvotes: 1
Views: 12069
Reputation: 3197
This is the way I believe:
UPDATE users SET id = (SELECT `AUTO_INCREMENT`
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'test'
AND TABLE_NAME = 'users') WHERE id = 2;
select * from users;
I used by own tables substitute yours.
test
is database name, users
is table name and id
is AUTO_INCREMENT
in my case.
EDIT: My Query above works perfect but its side effects are somewhat 'dangerous', upon next insert as AUTO_INCREMENT
value will collide with this recently updated record so just next single insert will fail. To avoid that case I've modified above query to a transaction:
START transaction;
UPDATE users SET id = (SELECT `AUTO_INCREMENT`
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'test'
AND TABLE_NAME = 'users') WHERE id = 2;
#renew auto increment to avoid duplicate warning on next insert
INSERT IGNORE INTO users(username) values ('');
COMMIT
Hope this will help someone if not OP.
Upvotes: 3
Reputation: 9042
You can use the REPLACE INTO
clause to do the trick.
From the manual:
REPLACE works exactly like INSERT, except that if an old row in the table has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted before the new row is inserted. See Section 13.2.5, "INSERT Syntax".
EDIT My mistake (in the comments) that you have to have two unique constraint to achieve this:
When you use the auto_increment value to REPLACE the record, the record will be replaced with the give ID and will not change (however the AI value will increment).
You have to exclude the AI column from the query. You can do that if you have one more UQ constraint.
Check this SQLFiddle demo: http://sqlfiddle.com/#!2/1a702e
The first query will replace all the records (but the id's value will not change).
The second one will replace it too, and the new AI value will be used. (Please note, that the second query does not contain the id
column, and there is a UQ constraint on the some
column).
You can notice, that the second query uses higher AI values than it is excepted: this is because the first replace incremented the AI value.
If you do not have two unique keys (one for the AI and one for another columns), the REPLACE statement will work as a normal INSERT statement!
(Ofcourse you can change one of the UNIQUE KEYs with a PRIMARY KEY)
Upvotes: 1
Reputation: 64476
The way you are trying to update same table is wrong but you can use join on same table
update idtest t
join (select id +1 as id
from idtest order by id desc
limit 1) t1
set t.id=t1.id
where t.id=3;
or
update idtest t
join (select max(id) +1 as id
from idtest ) t1
set t.id=t1.id
where t.id=3;
Upvotes: 2