How to handle fragmentation of auto_increment ID column in MySQL - mysql

I have a table with an auto_increment field and sometimes rows get deleted so auto_increment leaves gaps. Is there any way to avoid this or if not, at the very least, how to write an SQL query that:
Alters the auto_increment value to be the max(current value) + 1
Return the new auto_increment value?
I know how to write part 1 and 2 but can I put them in the same query?
If that is not possible:
How do I "select" (return) the auto_increment value or auto_increment value + 1?

Renumbering will cause confusion. Existing reports will refer to record 99, and yet if the system renumbers it may renumber that record to 98, now all reports (and populated UIs) are wrong. Once you allocate a unique ID it's got to stay fixed.
Using ID fields for anything other than simple unique numbering is going to be problematic. Having a requirement for "no gaps" is simply inconsistent with the requirement to be able to delete. Perhaps you could mark records as deleted rather than delete them. Then there are truly no gaps. Say you are producing numbered invoices: you would have a zero value cancelled invoice with that number rather than delete it.

There is a way to manually insert the id even in an autoinc table. All you would have to do is identify the missing id.
However, don't do this. It can be very dangerous if your database is relational. It is possible that the deleted id was used elsewhere. When removed, it would not present much of an issue, perhaps it would orphan a record. If replaced, it would present a huge issue because the wrong relation would be present.
Consider that I have a table of cars and a table of people
car
carid
ownerid
name
person
personid
name
And that there is some simple data
car
1 1 Van
2 1 Truck
3 2 Car
4 3 Ferrari
5 4 Pinto
person
1 Mike
2 Joe
3 John
4 Steve
and now I delete person John.
person
1 Mike
2 Joe
4 Steve
If I added a new person, Jim, into the table, and he got an id which filled the gap, then he would end up getting id 3
1 Mike
2 Joe
3 Jim
4 Steve
and by relation, would be the owner of the Ferrari.

I generally agree with the wise people on this page (and duplicate questions) advising against reusing auto-incremented id's. It is good advice, but I don't think it's up to us to decide the rights or wrongs of asking the question, let's assume the developer knows what they want to do and why.
The answer is, as mentioned by Travis J, you can reuse an auto-increment id by including the id column in an insert statement and assigning the specific value you want.
Here is a point to put a spanner in the works: MySQL itself (at least 5.6 InnoDB) will reuse an auto-increment ID in the following circumstance:
delete any number rows with the highest auto-increment id
Stop and start MySQL
insert a new row
The inserted row will have an id calculated as max(id)+1, it does not continue from the id that was deleted.

As djna said in her/his answer, it's not a good practice to alter database tables in such a way, also there is no need to that if you have been choosing the right scheme and data types. By the way according to part od your question:
I have a table with an auto_increment field and sometimes rows get deleted so auto_increment leaves gaps. Is there any way to avoid this?
If your table has too many gaps in its auto-increment column, probably as a result of so many test INSERT queries
And if you want to prevent overwhelming id values by removing the gaps
And also if the id column is just a counter and has no relation to any other column in your database
, this may be the thing you ( or any other person looking for such a thing ) are looking for:
SOLUTION
remove the original id column
add it again using auto_increment on
But if you just want to reset the auto_increment to the first available value:
ALTER TABLE `table_name` AUTO_INCREMENT=1

not sure if this will help, but in sql server you can reseed the identity fields. It seems there's an ALTER TABLE statement in mySql to acheive this. Eg to set the id to continue at 59446.
ALTER TABLE table_name AUTO_INCREMENT = 59446;
I'm thinking you should be able to combine a query to get the largest value of auto_increment field, and then use the alter table to update as needed.

Related

Insert into on duplicate key - auto increment id skipped

I currently have an SQL execution script which updates the row on duplicate key which looks like this.
$stmt = $dbCon->prepare("INSERT INTO videos_rating (videos_rating_video_fk, "
. " videos_rating_user_fk, "
. " videos_rating_rating) "
. " VALUES (:video_id, "
. " :user_id, "
. " :video_rating) "
. " ON DUPLICATE KEY UPDATE videos_rating_rating = :video_rating");
The script works fine but is there a way to prevent the auto increment column of getting out of sync?
Lets assume we start with an empty table, i then rate a video which then creates a row which will get the id of 1, then the user execute the SQL again by rating the same video a lower or higher rating and the row will be updated because its now a duplicate key, sure no problem.
The problem is this.
Next time another user rates a new new video the row will now begin at id 3 and not 2?
The table will then look like this
id | videos_rating_user_fk | videos_rating_rating
1 | 1 | 4
3 | 2 | 5
I were not able to find a similar question even tho i find it higly unlikely that no one else has been bothered with this, if so please refer me over to that post.
I know ids are not supposed to 'look good' but it is very annoying that ids jump from 30 - 51 - 82 - 85 - 89 etc and would there not be a problem at some point when the maximum UNSIGNED big int number is reached? im not saying i will ever go that high but still.
I assume that you are using the default InnoDB engine. In that case the "problem" is that the engine will "reserve" the id before it knows if it's a duplicate or not. Once the id is "reserved" it cannot be released, because another thread (another user) might perform an insert into the same table at the "same" time. There are also other ways to get gaps in the AUTO_INCREMENT column without deleting any rows. One is when you roll back a transaction.
You can try to "reset" the next AUTO_INCREMENT value after every insert with
alter table videos_rating auto_increment = 1;
But I can't say what problems you might run in executing this statement in a running live environment. And I'm not going to find that out.
Note that this is usually not an issue, because tables on which you run IODKU statemts (usually) don't need an AUTO_INCREMENT column. As Cid wrote in his answer, you can just drop the id column and define your unique key as primary key.
Let's assume your table is built this way :
videos_rating_video_fk | videos_rating_user_fk | videos_rating_rating
-----------------------+-----------------------+----------------------
The first key videos_rating_video_fk should be a foreign key and not a primary key with autoincrement.
If users 1 and 2 vote for the video that has the id 1, your table should looks like this :
videos_rating_video_fk | videos_rating_user_fk | videos_rating_rating
-----------------------+-----------------------+----------------------
1 | 1 | 4
1 | 2 | 5
For that kind of table, the primary key should be the combination of both foreign keys and will be unique. A user can vote only once for a video (unique vote = unique key). A video can be voted by multiples users and users can vote for multiples videos.
I suggest you to take a look at the Merise Method for building tables with integrity constraints and creation of primary keys.
Live with the "burning" of ids. AUTO_INCREMENT guarantees not to allow duplicate values, not does not provide any other guarantees.
There are about 4 other ways where ids may be 'burned': REPLACE, Multi-Master / Galera, IGNORE, DELETE, and possibly more.
IODKU quickly grabbed an id before discovering that the statement would turn into an UPDATE and not need the id. To do otherwise would probably be a significant performance hit.
To confirm, Paul Spiegel's answer helped my to resolve the issue. I had some an 'Upsert' SQL query that used ON DUPLICATE KEY UPDATE to determine whether to create a new row or update an existing row. Where a row was updated frequently, the jumps in assigned Id's where large.
the "problem" is that the engine will "reserve" the id before it knows if it's a duplicate or not.
I resolved the problem by breaking the SQL code into separate INSERT and UPDATE statements. I'm no longer seeing the issue.

Syncronizing entries in a table according to a user array in MySQL

I have the following situation. The entries in a table come from a list of users for a particular id. That way id 7 has 3 rows for, say, 2, 6, 7 (these three are unique ids for a user data table). To clarify the table looks like this.
ID USERID KEYID
Where KeyID is auto_inc and is the table's primary key.
These entries come from a multiple select field. So a user might want to delete user 2 and add user 8. So the function that needs to update the table gets the array 8,6,7 for id 7. The quick way I found to do the syncronization is simply to delete every entry in the table for ID 7 and add 3 new entries of the from (ID,USERID) (7,8) (7,6) (7,7).
However I don't know if this is how it's supposed to be. Is there a better way? Also this methods drives the keyid up really fast (for every modification in the table, basically). Is that a problem? I'm an newbie with these things, so please be patient.
Well, you can delete rows selectively like this:
DELETE FROM my_table WHERE user1 = 7 AND user2 NOT IN (8,6,7)
where (8,6,7) is list of pairs (7,6),(7,8),(7,7) , that should be preserved, kept, not deleted, and it will delete all other pairs (7,?)
Your next question may be "Then how to add pairs while not making duplicities?"
You will first need to teach your table what duplicity is. By adding unique key on pair of fields (user1,user2). That would forbid duplicities. Now, when inserting new rows that may be already there, use "INSERT IGNORE" to ignore such exception and simply, continue on.

Combine Access tables

Before you think this is a question that has already been answered, hear me out.
I inherited an Access MDB file. It has 2 tables (actually 4 but I'm only really concerned with 2). One is a member table with an AutoNumber field. The second table has the same fields but the ID field is simply Number (not AutoNumber). This second table gets the names of people when they "retire".
The problem now is some people want to return. I can't copy them back in because they will get a new ID number and all of the other report data (that I didn't mention before) is keyed to their original ID number which is still unique between the two tables.
What I think I'd like to do is temporaily turn off the AutoNumber field long enough to allow me to merge the data from the "retired" table, then turn AutoNumber back on. But Access won't let me do that because the AutoNumber ID is tied to other tables and various reports. (I guess I do care about the other tables.)
This answer is close, Adding records with old ids that were generated using auto number in access, but focuses on 1 row. I have plenty. Same with this answer: Keep value of autonumber column when importing into Microsoft Access database.
I'm thinking the second answer is really close, but I don't know how to use docmd.RunSQL.
If this is simple and I'm just missing the obvious, I'm willing to admit being a NOOB with Access.
Access will let you INSERT a row with a number for an autonumber field as long as the number you're inserting doesn't conflict with any of the existing values. And since "their original ID number which is still unique between the two tables", it sounds like that is your situation.
Consider these 2 tables:
tblMembers
id fname
1 Anne
3 Cathy
tblRetired
id fname
2 Bob
4 David
This INSERT statement (the Access query designer calls it an "append query") will add the tblRetired rows to tblMembers.
INSERT INTO tblMembers ( id, fname )
SELECT r.id, r.fname
FROM tblRetired AS r;
This is tblMembers after executing that INSERT ...
id fname
1 Anne
2 Bob
3 Cathy
4 David

how generate serial number in mysql database?

I have table like this:
table1:
sr no. | id | name
id is the primary key here.
I want sr no. to be cyclic.
What attributes i should give to sr no. field?
For example I have 5 records with sr no 1,2,3,4,5 and id 1,2,3,4,5 and i delete 3rd record then id will be 1,2,4,5 and sr no. will be 1,2,3,4
sr no should not lost its contentiousness.
SELECT #a:=#a+1 serial_number,
name FROM table1,
(SELECT #a:= 0) AS a;
I do not know about the real purpose of sr no.
But you can create Trigger on DELETE operation for this table, now you can update the records which are greater than current ID so that sr no is decremented by 1.
e.g. you can write below SQL in Trigger,
UPDATE table1 SET sr_no = sr_no -1 WHERE id > XYZ;
id is already your primary key, so what do you need sr no. for? Would suggest to forget about it.
If you really need it, see my answer here for the several reasons why this is a really bad idea. The most important reason:
There are reasons why MySQL doesn't automatically decrease the
autoincrement value when you delete a row. Those reasons are
danger of broken data integrity (imagine multiple users perform deletes or inserts...doubled entries may occur or worse)
errors may occur when you use master slave replication or transactions
and so on ...
No need to worry about gaps.
To answer your question how to do it, just add auto_increment in the column definition and include it in the primary key.

How can I change auto increment primary key values on row deletion?

I have a problem that whenever I delete a row, the row ID corresponding to that row gets deleted, but I don't want this. What I want is if any row is deleted, then other rows after that row should shift one (the no. of rows deleted) position up.
Example:
Suppose there is a user table(id and name)
id(auto incremented primary key) name
1 xyz
2 aaa
3 ray
4 mark
5 allen
now delete row with id=3 and table should look like
id(auto incremented primary key) name
1 xyz
2 aaa
3 mark
4 allen
Is there any way to accomplish this?
No! Don't do this!
Your Autoincrement ID is the IDENTITY of a row. Other tables use this ID to refer to a certain row. If you update the ID, you would have to update all other tables referencing this row, which is not at all the point of a relational database.
Furthermore, there never is a need to do this: you won't run out of autoincrement columns fast (and if you do, just pick a bigger datatype).
An autoincrement ID is a purely technical number, your application users should never see or use it. If you want to display an identificator to your users, add another column!
You've completely got the wrong end of the stick. Auto numbers should not be changed as this would break the link between any other referencing tables.
What you want, by the sounds of it, is a row counter, not a primary key.
While its generally not recommended to change these values, there do exists instances where you may need to change them. If you have the appropriate Foreign Key relationships setup to cascade on UPDATE then you could do this. Granted you need to be 100% all FK relationships are defined as expected.