prevent gaps in MySQL id field - mysql

I have a MySQL table with an auto incremement id field. When I delete a row and then insert a new row, The id of the row I deleted is skipped and the new gets an id of one greater than the previous row. Is there any way I can prevent this? I would like the new row to just replace the old one. Is there an important reason why this happens that I am missing?

The MySQL auto-increment function NEVER goes backward unless you force it to. And for a good reason. What if there was stray references to the missing records (logs, tables, etc...)?
You can force it by using this command:
ALTER TABLE tbl AUTO_INCREMENT = 1000;
Or, if you need to do it as part of the query:
LOCK TABLES tbl WRITE;
SELECT #id := MAX(id) FROM tbl;
INSERT INTO tbl SET id=#id, ...;
UNLOCK TABLES;
If you are using InnoDB, you could do this in a transaction instead...
Better to leave it be, however.

The definition of an autoincrement field is that every new row inserted is guaranteed to get a unique value. If you want to keep the old value then you must UPDATE the row instead of replacing it. If your design requires that autoincrement column values be contiguous then you will have to manage that yourself.

I'm sorry but I don't know the exact reason.
AFAIK you can't avoid that behavior unless you TRUNCATE the table or explicitly specify the id.

Related

SQL Table - How to add a row at the start of an old autoincrement column

I have an existing sql table with 3 columns and 100+ entries/rows. There is an id column with autoincrement.
Now, I want to add 10 new rows at the beginning of the table with id from 1 to 10. But I cannot lose any existing row. So, how do I do it?
One idea that just came to my mind is perhaps I can increase the existing id by adding 10, like 1+10 becomes 11, 25+10 becomes 35, and then I can add rows at the beginning. What will be the script for this IF this is possible?
All you need to do for this is to set the auto_increment for that table to whatever number you need to create space for the new records you want to insert.
For example, if you inserted rows with id's 1-100, you might:
Check the next auto_increment value by running:
select auto_increment as val from information_schema.tables where table_schema='myschema' and table_name='mytable';
Let's assume that value would be 101 (the value that would be used if you inserted a new row). You can "advance" the auto_increment value by running:
alter table myschema.mytable auto_increment = 111;
If you insert a new row like this:
insert into mytable (not_the_id_column) values ('test');
It will get the "next" id of 111. But if you specify id values manually, you are ok in this case as long as you use any value less than 111, so you could insert your desired records like this:
insert into mytable (id, not_the_id_column) values (101, 'test101');
insert into mytable (id, not_the_id_column) values (102, 'test102');
... -- more inserts as needed
Now, you still must take proper precautions when updating PK values, or any value that has dependencies on it (Foreign Key or otherwise), but it is completely legitimate to forcibly advance and/or backfill the id values, as long as the resulting auto_increment value doesn't duplicate one that's already in the table.
I agree with juergen d's comment that you should not do this, but I realize there are situations where this kind of thing must be done.
SELECT MAX(id)-MIN(id)+1 INTO #x FROM theTable;
UPDATE theTable SET id = id + #x;
SELECT MIN(id) INTO #x FROM theTable;
UPDATE theTable SET id = 10 + id - #x;
If the id is the primary key, value collisions within an update can cause MySQL to reject the update. (Hence the pair of updates to avoid such a possibility.)
Edit: Factoring N.B.'s strong objection into this, it would also probably be good to verify the table's next auto-increment value is not going to collide with the updated records after the update is completed. I don't have an appropriate database on hand to verify whether UPDATE statements affect it; and even if they do affect it, you may end up wanting to reduce it so as to not create an unnecessary gap (gaps should ideally not be a problem, but if they are or you are just mildly OCD, it is worth looking into).

Can i adjust the value of an auto-incremented field in the database automatically?

Can i adjust the value of an auto-incremented field in the database automatically?
I have a table called "post" which has a field called "pid" which is set to auto-increment.
Posts from this table may be deleted by the user at a later time, but the auto- incremented value will not be adjusted. Is there a way to adjust the pid field everytime posts are deleted?
for eg:If i have 4 entries: pid=1,2,3,4(pid-auto-increment)
Now if i delete 2, is there a way to update 3 to 2 and 4 to 3 and so on ?
Why would you need to adjust the auto-increment? Each post is uniquely identified using the pid and if that is to change, then the whole DB structure will fail. The idea of the auto-increment is based on this principle and that you don't have to worry about assigning numbers yourself.
If deleting a record is a problem, then you might want to keep it in the database and flag it as deleted. They you can use this flag to show / hide from the users.
Deletion from end
You can manually set AUTO_INCREMENT of a table to a specified value via
ALTER TABLE tbl AUTO_INCREMENT = val;
See Using AUTO_INCREMENT in MySQL manual.
This solves deletion from end – before adding new rows, set AUTO_INCREMENT to 0 and it will be automatically set to current maximum plus one. Newly inserted rows will occupy the same IDs as the deleted ones.
Deletion from anywhere – renumbering
It is possible to manually specify value of the field having AUTO_INCREMENT. AUTO_INCREMENT is ignored them. If you specify a value already used, unique constraint will abort the query. If you specify a value that is bigger than the current maximum, AUTO_INCREMENT automatically set to this one plus one.
If you do not want to manually renumber the records, write a script for that, nor mess with stored procedures, you can use user-defined variables:
SET #id = 0;
UPDATE tbl SET id = #id := #id + 1 ORDER BY id;
SET #alt = CONCAT('ALTER TABLE tbl AUTO_INCREMENT = ', #id + 1);
PREPARE aifix FROM #alt;
EXECUTE aifix;
DEALLOCATE PREPARE aifix;
Example use
http://www.paulwhippconsulting.com.au/webdevelopment/31-renumbering-an-qorderingq-field-in-mysql
http://www.it-iss.com/mysql/mysql-renumber-field-values/
For more info see my answer to a related question.
Warning – this may be harmful!
Usually there is no need to renumber the records. Actually it may be harmful as you may have dangling references to the old record somewhere (possibly outside the DB) and they now become valid again, which could cause confusion. This is why AUTO_INCREMENT attribute of the table is not decremented after a row is deleted.
You should just delete the unwanted records and stop worrying about the holes. They are just in the numbering of the records, purely logical, physically they don’t need to exist in the storage. No space wasted in the long time perspective. For some time the storage really has holes. You can let the DB engine get rid of them by OPTIMIZE TABLE tbl or ALTER TABLE tbl ORDER BY column.

Fix following id's when a row is deleted

I have a table with some rows, each row has a unique key. When a row is deleted from the table, all rows that are below this row should be 'moved up'. Is there some built in function in MySQL that does this or should I just do it with PHP or perhaps UPDATE table SET id=id-1 WHERE id > deletedid?
Using the last one seems a bit messy.
What would be the best way to do this?
Why do you want to do this? I know it's ugly to have holes in your unique ID sequence, but the downside of invalidating any references to IDs from outside the database is normally very much greater. The normal thing is to just accept the sequence won't be contiguous. If these represent a sequence, consider just sorting by the order rather than expecting the N'th value to have value N (any sort of iteration should provide its own index somewhere for this use).
If the value is one you set yourself, and you definitely want to keep it as having values from 1 to N (N="number of rows"), and you want to keep the sequence of values even if they're not in the order the rows were inserted, then "UPDATE table SET id=id-1 WHERE id > deletedid" is probably the best answer.
If the value is an auto_increment field, and you don't care which numbers go with with rows as long as each row has a number from 1 to N, you can alternatively do ALTER TABLE DROP COLUMN 'columnname' and then ALTER TABLE again to add the column again, and the database will regenerate the ids from 0. (Not necessarily in the same order, though it often is.)
There may be a way to renumber only the rows after that point, but (according to a quick google) it doesn't look like there's anything easier than what you're already planning.
First you have to ensure that the column is not a foreign-key for any other table.
Then you can try this (I am not 100% positive it will work):
DELETE FROM
MyTable
WHERE
id = deletedid;
UPDATE
table
SET
id=id-1
WHERE
id > deletedid
ORDER BY
id
As stated in mysql docs:
If the ORDER BY clause is specified,
the rows are updated in the order that
is specified.
and in this way you ensure uniqueness of the field.

Auto Increment Problem in Mysql

I created a table and set a field to auto increment some thing like this:
CREATE TABLE t1(id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE = MyISAM AUTO_INCREMENT = 123456;
But to some reason i deleted some of the rows in the table.
Now the question is when i insert new rows in the table the new rows should be assigned id's of the rows which have been deleted rather than assigning new id's.
I do not want to reset all the id's
How can i do this??
Help appreciated:)
Sorry to say, but that is not the use of AUTO_INCREMENT. If you want to re-use id's, then you would have to write your own trigger functions, and doing this is generally considered bad practice.
Imagine you were on id 50,000, and deleted an entry with id 1... would you really want the next record you add to re-use id 1?
The whole point of AUTO_INCREMENT is to auto increment...
You can explicitly assign these ids though and mysql will allow it.
You are going to have to do this manually rather than rely on MySQL to do it for you. The AUTO-INCREMENT flag keeps an integer that is incremented upon every insert statement and is assigned as the PK of the subsequent insert. Unless you want to write an update trigger that resets this value to the lowest non-used integer, I would suggest processing this in a server-side scripting language.
In any case, though, why is using the auto increment value a problem?
To reset the autoincrement value, you can use
ALTER TABLE t1 AUTO_INCREMENT=1
The next inserted record will use ID 1.
This might be something you're after.
alter table Users AUTO_INCREMENT=0;
This will reset the auto_increment back to 0 + whatever the current highest id is.
if you have 30, your next entry would be 31

How to insert the value derived from AUTO_INCREMENT into another column in the same INSERT query?

I have an id column which is a primary key with AUTO_INCREMENT. I need the value that is generated to be inserted into the id column, as well as another column (which isn't set to AUTO_INCREMENT, and isnt unique.
Currently I use the mysqld_isnert_id() function to get the id, and simply run an update query after the insert, but I was wondering if I could do this without running the 2nd update query.
after insert Trigger?
If I recall correctly, the automatically generated ID isn't even created until after the insert has been performed. Your two query way is probably the only way without diving into perhaps a stored procedure.
You could define a trigger along the lines of:
delimiter //
CREATE TRIGGER upd_check AFTER INSERT ON mainTable
FOR EACH ROW
BEGIN
UPDATE dependingTable
SET dependingTable.column = NEW.id
END;//
delimiter ;
I am not exactly sure WHEN the AUTO_INCREMENT value is generated, but you could try the following, since if it works it'll save you an update (If the column you want the value replicated to is in the same row as the inserted row):
CREATE TRIGGER upd_check BEFORE INSERT ON mainTable
FOR EACH ROW
SET NEW.column = NEW.id
The only way I can see you doing it with a single query is to use the information schema. In the information schema there is a table called 'tables', there you access the column auto_increment. That contains the NEXT insert id for that table, you can access this via a nested select, just give the user used to connect to the database read access to that table. This will only work with innodb engines as far as I can tell as that way the nested select you'll do to populate the second id field will be part of the greater transaction of the insert.
That's what your query might look like:
INSERT INTO fooTable VALUES (0, (SELECT AUTO_INCREMENT FROM information_schema.TABLES));
Also if you're worried about read access and security issues, just remember this is the same info you can get by running a show table status. Speaking of which, I tried to see if you could project the show commands/queries via a select and you can't, which totally sucks, because that would have been a much cleaner solution.