How mysql AUTO_INCREMENT works? - mysql

In mysql AUTO_INCREMENT doc, I didn't find the detail explanation about how it pick new id on insert,
Is it cache a "max number" key, or find the smallest ID which is not in use? any reference to the source code?
EDIT:
I had test it, looks like it will not reuse deleted item's ID, But I'm not sure. I want use id as a timestamp for ordering, so I must 100% certain it is so, that means this logic explanation should has a trusted reference, either in source code or in mysql doc..

It will NOT pick the smallest number not in use. It will always increment one to the last ID it had assigned. This will be a constant time operation. (As opposed to picking the smallest number not in use which would be linear time*).
For example, if you insert 10 rows, then delete the 5th row. The next inserted row will get an ID of 11, and NOT 5.
This operation can quite easily be optimized to be constant time as well (at the time of insertion) by accepting a penalty at the time of deletion. However, that is not what MySQL does.

Yes, that's the way auto_increment works.
The value will be incremented for each new row
The value is unique, duplicates are not possible
If a row is deleted, the auto_increment column of that row will not be re-assigned.
The auto_increment value of the last inserted row can be accessed using the mySQL function LAST_INSERT_ID() but it must be called right after the insert query, in the same database connection.
http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

Related

How to create SQL table with limited entries?

I have a table that is used to store the latest actions the user did (like a ctrl+z for the program), but I want to limit this table to about 200 entries, and after that, every new entry would delete the oldest in the table.
Is there any option to make the table behave this way on SQL or do I need to add some code to the program to do it?
I've seen this kind of idea before, but I've rarely seen a case where it was a good idea.
Your table would need these columns in addition to columns for the normal data.
A column of type integer to hold the row number.
A column of type timestamp (standard SQL timestamp) to hold the time of the last update.
The normal approach to limit this table to 200 rows would be to add a check constraint to the column of row numbers. For example, CHECK (row_num between 1 and 200). MySQL doesn't enforce check constraints, so instead you'll need to use a foreign key reference to a table of row numbers (1 to 200).
All insert statements will need to determine whether the table is full, examine the time of the last update, and either a) insert a new row with a new row number, or b) delete the oldest row or overwrite it.
My advice? Renegotiate this requirement.
Assuming that "200" is not a hard limit, in other words if the number of entries occasionally went over that by a small amount it would be OK...
Don't do the pruning on line, do it as an off line process, run as often as needed to keep the totals per user from not getting "too high".
For example, one such solution would be to fire the SQL that does that query every hour using crontab.

Auto Increment Manually

There is a table with an int field - field_1.
I want to insert a new row.
The field_1 value will be Maximum value from all the entries plus one.
I've tried:
INSERT INTO table (field names, `field_1`)
VALUES (values, '(SELECT MAX(field_1) FROM table)');
I get '0' in the field_1.
I know I can do it in separate queries.
Is there a way to perform this action with one query? I mean one call from php.
I have an auto-increment field 'id' and I want to add 'position' field. I want to be able to make changes in position but the new item will always have highest position
Whatever it is that you are trying to do, it will not work, because it is not guaranteed to be atomic. So two instances of this query executing in parallel are guaranteed to mess each other up at some random point in time, resulting in skipped numbers and duplicate numbers.
The reason why databases offer auto-increment is precisely so as to solve this problem, by guaranteeing atomicity in the generation of these incremented values.
(Finally, 'Auto Increment Manually' is an oxymoron. It is either going to be 'Auto Increment', or it is going to be 'Manual Increment'. Just being a smart ass here.)
EDIT (after OP's edit)
One inefficient way to solve your problem would be to leave the Position field zero or NULL, and then execute UPDATE table SET Position = Id WHERE Position IS NULL. (Assuming Id is the autonumber field in your table.)
An efficient but cumbersome way would be to leave the Position field NULL when you have not modified it, and give it a value only when you decide to modify it. Then, every time you want to read the Position field, use a CASE statement: if the Position field is NULL, then use the value of Id; otherwise, use the value of Position.
EDIT2 (after considering OP's explanation in the comments)
If you only have 30 rows I do not see why you are even trying to keep the order right on the database. Just load all rows in an array, programmatically assign incrementing values to any Position fields that are found to be NULL, and when the order of the rows in your array changes, just fix the Position values and update all 30 rows in the database.
Try this:
INSERT INTO table (some_random_field, field_to_increment)
SELECT 'some_random_value', IF(MAX(field_to_increment) IS NULL, 1, MAX(field_to_increment) + 1)
FROM table;
Or this:
INSERT `table`
SET
some_random_field = 'some_random_value',
field_to_increment = (SELECT IF(MAX(field_to_increment) IS NULL, 1, MAX(field_to_increment) + 1) FROM table t);
P.S. I know it's 4 years late but I was looking for the same answer. :)
ALTER TABLE table_name AUTO_INCREMENT = 1 allows the database to reset the AUTO_INCREMENT to:
MAX(auto_increment_column)+1
It does not reset it to 1.
This prevents any duplication of AUTO_INCREMENT values. Also, since
AUTO_INCREMENT values are either primary/unique, duplication would
never happen anyway. The method to do this is available for a reason.
It will not alter any database records; simply the internal counter so
that it points to the max value available. As stated earlier by
someone, don't try to outsmart the database... just let it handle it.
It handles the resetting of AUTO_INCREMENT very well. See gotphp

How to avoid duplicate entries on INSERT in MySQL?

My application is generating the ID numbers when registering a new customer then inserting it into the customer table.
The method for generating the ID is by reading the last ID number then incrementing it by one then inserting it into the table.
The application will be used in a network environment with more than 30 users, so there is a possibility (probability?) for at least two users to read the same last ID number at the saving stage, which means both will get the same ID number.
Also I'm using transaction. I need a logical solution that I couldn't find on other sites.
Please reply with a description so I can understand it very well.
use an autoincrement, you can get the last id issued with the mysql_insert_id property.
If for some reason that's not doable, you can craete another table to hold the last id used, then you increment that in a transaction, and then use it as the key for your insert into the table. Got to be two transctions though, otherwise you'll have the same issue you have now. That can get messy and is an extra level of maintenance though. (reset your next id table to zero when ther are still some in teh related table and things go nipples up quick.
Short of putting an exclusive lock on the table during the insert operation (not even slightly recomended), your current solution just can't work.
Okay expanded answer based on leaving schema as it is.
Option 1 in pseudo code
StartTransaction
try
NextId = GetNextId(...)
AddRecord(NextID...)
commit transaction
catch Primary Key Violation
rollback transaction
Do the entire thing again
end
Obviously you could end up in an infinite loop here, unlikely but possible, probably run out of stack space first.
You could some how queue the requests and then attempt to process them, if successful remove from queue.
BUT make customerid an auto inc the entire problem dispappears.
It will still be the primary key, you just don't have to work out what it needs to be any more, in fact you don't supply it in the insert statement, mysql will just take care of it for you.
The only thing you have to remember is if you need the id that has been automatically created is to request it in one transaction.
So your insert query needs to be in the form
Insert SomeTable(SomeColumns) Values(SomeValues)
Select mysql_insert_id
or if multiple statements gets in the way wrap two statements in a start stransaction commit transaction pair.

MySQL - Migrating some ID numbers over from randomly generated to autoincremental

I am in the process of rewriting a company's entire system. The original developer was a bit silly and generated ID numbers for each customer report randomly in his database. Each ID number is up to 7 digits long - but could be anything.
I am migrating over all his old data to our new, far more logically structured database. I obviously want to use a MySQL auto-increment for our ID field. However, it's vital that we keep the old ID numbers as customers still phone up each day with those to reference against.
Ideally, the perfect scenario would be we go live December 1st - everything up to December 1st is all randomly IDed, and from December 1st onwards they automatically increment starting at the highest random ID in the old database.
Is such a thing possible with MySQL without any issues? I am currently using two columns - one, our logical autoincrementing ID, and a second column called old_id which was being used during migration. But we need the call centre staff to only be using one ID or mass confusion will ensue.
Thanks!
If you start numbering from the highest random value, just changing the field to autoincrement should be enough, the normal behaviour is that mysql won't change ids already set, and starts numbering from the highest value+1.
If you want to start from a specific value (say 10,000,000) you can set
ALTER TABLE theTableInQuestion AUTO_INCREMENT=10000000
Of course, be sure to create backups and test, but it should not pose any problems at all. (Note that the old records will be stored in order of the id-field, which is random, and won't reflect the creation order.)
As you need to keep the old IDs, I'm going to assume that you're going to create a new column for autoincrement ID that will become your primary key but keep the existing ID column and rename it (to old_id, maybe?). I'm also going to assume you record when a customer signed up.
If you make your old ID column nullable (allow NULL as a valid value) then you can simply check whether or not the old ID column is NULL. If it's not NULL then treat that as the ID, otherwise use the autoincrement column.
Finding a customer:
SELECT *
FROM customer
WHERE (id = /*Put your ID here*/ AND reg_date >= /*Put the date the new regime starts here*/)
OR (id_old = /*put your ID here*/ AND reg_date < /*Put the date the new regime starts here*/)
This will occasionally return 2 rows so you'll have to use some other criteria to uniquely identify the customer in question.
As for associating an old customer with other tables in the database, you can always use the new ID internally throughout the entire DB once its generated. You will have to update tables that are using the old ID as the foreign key, obviously.
UPDATE target_table
JOIN customers on target_table.cust_id = customers.id_old
SET target_table.cust_id = customers.id;
(Note: The above is just a quick and dirty query that hasn't been tested! I'd suggest testing on a copy of the database before you try it for real!)

Will all numbers be 100% unique in a table if to use this simple algorithm?

My goal is to insert a new and unique(unique is very important) number into a MySQL table on the server every time upon an event on a user's machine, using ajax.
So, server's part on user's event is doing this (using php):
Finds a maximum value from the column in the db,
Adds 10 to a maximum value,
this is a new and unique (bigger than a maximum) value, we insert insert into a table.
Will all numbers be unique and go like 1, 11, 21, 31, if it starts from 1? I'm curious if inserting into the Table finishes before it starts performing another queue and coule be like 1, 11, 21, 21, 31, 41?
If it theoretically works like this (ordered by time)
find max value from the column for the first user
find max a value from the column for the second user (it will be the same)
insert a (max+10) for the first user into the same table
insert a (max+10) for the second user into the same table (it will be the same), then the results will be the same, and 1 value could be repeated twice or even more...
So, the question is: will all numbers be 100% unique?
Depending on this I have to choose which algorithm to use for creating unique numbers.
Added:
Is it possible to be sure with this algorythm and without using autoincrements? Autoincrement is used for another column. Holes between numbers are OK. The only requirement is that numbers should be different, but with some "delta" that is more than one. Sorry I didn't notice about that in my question. Thank you.
Unless you have a very specific reason against it, I recommend using AUTO_INCREMENT - it will scale much better and actually leave fewer "holes" in the sequence of numbers than your approach.
And you are correct - your approach will not actually guarantee uniqueness in concurrent environment. One way to make your algorithm work is to have a UNIQUE constraint on your field (if it is not already PRIMARY KEY) and then repeatedly attempt to insert a new value - if it fails just generate a new value and try again and it will eventually succeed.
Use an auto incrementing column in the db
It sounds like you want to use autoincrement.
If you're using auto-increment on a separate column, you can still emulate it with something like the following.
insert into mytable (
column1,
column2,
fake_auto_incr
) select
'value for column1',
'value for column2',
max (fake_auto_incr) + 1
from mytable
Because the insert is a transactional statement, ACID databases will ensure the max+1 trick is always one greater then the current top value.
Keep in mind you'll need a slight adjustment for that to work on an empty table, since the query will return NULL in that case. This should suffice:
insert into mytable (
column1,
column2,
fake_auto_incr
) select
'value for column1',
'value for column2',
coalesce (max (fake_auto_incr), 0) + 1
from mytable
This forces the initial value to 1 on an empty table, otherwise it uses the next available value.