AUTO_INCREMENT not working as expected [duplicate] - mysql

I've been using InnoDB for a project, and relying on auto_increment. This is not a problem for most of the tables, but for tables with deletion, this might be an issue:
AUTO_INCREMENT Handling in InnoDB
particularly this part:
AUTO_INCREMENT column named ai_col: After a server startup, for the first insert into a table t, InnoDB executes the equivalent of this statement:
SELECT MAX(ai_col) FROM t FOR UPDATE;
InnoDB increments by one the value retrieved by the statement and assigns it to the column and to the auto-increment counter for the table.
This is a problem because while it ensures that within the table, the key is unique, there are foreign keys to this table where those keys are no longer unique.
The mysql server does/should not restart often, but this is breaking. Are there any easy ways around this?

If you have a foreign key constraint, how can you delete a row from table A when table B references that row? That seems like an error to me.
Regardless, you can avoid the reuse of auto-increment values by resetting the offset when your application starts back up. Query for the maximum in all the tables that reference table A, then alter the table above that maximum, e.g. if the max is 989, use this:
alter table TableA auto_increment=999;
Also beware that different MySQL engines have different auto-increment behavior. This trick works for InnoDB.

So you have two tables:
TableA
A_ID [PK]
and
TableB
B_ID [PK]
A_ID [FK, TableA.A_ID]
And in TableB, the value of A_ID is not unique? Or is there a value in TableB.A_ID that is not in TableA.A_ID?
If you need the value of TableB.A_ID to be unique, then you need to add a UNIQUE constraint to that column.
Or am I still missing something?

Use a foreign key constraint with 'SET NULL' for updates and deletes.

Create another table with a column that remembers the last created Id. This way you don't have to take care of the max values in new tables that have this as foreign key.

I checked.
alter table TableA auto_increment=1;
does NOT work.
And the reason I found in two documents
http://docs.oracle.com/cd/E17952_01/refman-5.1-en/innodb-auto-increment-handling.html
InnoDB uses the following algorithm to initialize the auto-increment counter for a table t that contains an AUTO_INCREMENT column named ai_col: After a server startup, for the first insert into a table t, InnoDB executes the equivalent of this statement:
SELECT MAX(ai_col) FROM t FOR UPDATE;
InnoDB increments the value retrieved by the statement and assigns it to the column and to the auto-increment counter for the table. By default, the value is incremented by one. This default can be overridden by the auto_increment_increment configuration setting.
and
http://docs.oracle.com/cd/E17952_01/refman-5.1-en/alter-table.html
You cannot reset the counter to a value less than or equal to any that have already been used.
This is the reason why alter table will not work. I think that only option is to wipe out data and rewrite it in a new table with new id.
In my case table was logfile , so I just did:
RENAME TABLE SystemEvents To SystemEvents_old;
CREATE TABLE SystemEvents LIKE SystemEvents_old;

Related

MSQL unique constraint check only onward insert records Not already existing records

The table already has duplicates entries. I want to create a unique constraint in MQSL DB without deleting the existing duplicates. If any duplicate entries coming onwards then it will show an error. Given blow queries not working in MYSQL.
ALTER TABLE presence
ADD CONSTRAINT present uniqueness UNIQUE (employee_id,roll_number) where id >10000;
or
ALTER TABLE presence
ADD CONSTRAINT present uniqueness UNIQUE (employee_id,roll_number) where id <> (343,34534,34534)
Do we have something like that solution in SQL?
Add an additional column to the table that indicates the existing values.
Set it to NULL for the existing values. And give it a constant value, say 1, for the new rows. Then create a unique index or constraint on this column:
alter table t add constraint unique (employee_id, is_old)
Actually, I realize that you probably don't want duplicates with singleton old values and new values. That is just an issue of setting the value to NULL only for duplicates in the history. So, one row would have a constant value (say 1) in the historical data.
MySQL allows duplicate values on NULL, which is why this works.

How to prevent mysql AUTO_INCREMENT value from being reset on server restart? [duplicate]

http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
That document I'm reading seems to say something like:
"In this case (when the AUTO_INCREMENT column is part of a multiple-column index), AUTO_INCREMENT values are reused if you delete the row with the biggest AUTO_INCREMENT value in any group."
I don't really understand what's being said there. Aren't the values supposed to be reused automatically?
Thanks in advance...
InnoDB resets the auto_increment field when you restart the database.
When InnoDB restarts, it finds the highest value in the column and then starts from there.
This won't happen in MyISAM because it caches the last incremented id.
Update
This feature/bug has been around since 2003 and can lead to serious issues. Take the example below,
Table t1 has an auto-inc primary key.
Table t2 has a column for the primary key in t1 without a foreign key "constraint". In other words, when a row is deleted in t1 the corresponding rows in t2 are orphaned.
As we know with InnoDB restart, an id can be re-issued. Therefore orphaned rows in t2 can be falsely linked to new rows in t1.
This bug has been finally fixed in MySQL 8.0.0 WL#6204 - InnoDB
persistent max value for autoinc columns.
InnoDB will keep track of the maximum value and on restart preserve
that max value and start from there.
When you have a primary key field that is also auto_increment, no matter how many rows (or in what order) are removed, the ids that no longer exist are not used again, and the field is incremented continuously for each row.
However, when your primary key consists of multiple fields (E.G. an auto_increment field and another field), the auto_increment field is reused, and only when the deleted id is the last id. What this means is if you have values like 1, 2, 3, 4, and 5, and you remove the row with the field value of 5, the next row will have an id of 5. However, if you remove the row with the id of 2, this will, again, not be used, and the next row will have an id of 6.
As described with the InnoDB engine the last PK utilised will be reused if the row is deleted prior to a reboot as it is not cached. If this PK is a Foreign Key (FK) in another table problems can arise (FK hell). This is how I discovered the problem when a little old lady shot up to 195 cm(!) as the deleted person's additional data was picked up. The work around for this is either to implement 'ON DELETE CASCADE' for the child tables or (not my preferred option) to code around this issue.
As mentioned in answer :
InnoDB resets the auto_increment field when you restart the database.
When InnoDB restarts, it finds the highest value in the column and then start from there.
and this behavior could lead to Foreign key problem.
Fortunately from MySQL 8.0.0 it will be fixed.
More info:
Bug #199 Innodb autoincrement stats los on restart
WL#6204: InnoDB persistent max value for autoinc columns
See BUG#199 on MySQL bugs.
Currently InnoDB does the following when a table
is opened: SELECT MAX(c) FROM t; where c is the AUTOINC column name.
This step is used to initialise the column's next autoinc value and
allocation of autoinc values starts from this point. InnoDB also does this
when it executes 'ALTER TABLE AUTO_INCREMENT=N;'.
...
InnoDB should keep track of the maximum value and on restart preserve
that max value and start from there.
If the table is of InnoDB type, then the deleted ids are reused in certain scenarios. Here is what I did to test it:
Table user has id column as primary key, with auto_increment set
CREATE TABLE user (
id bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
) ENGINE=InnoDB AUTO_INCREMENT=2108 DEFAULT CHARSET=latin1;
My createUser API call, creates a entry in user table. Lets say user with id = 1 was created.
Call deleteUser API which just deletes row from user table.
Stop and start the database
Call createUserAPI call again. It creates user with same id (id = 1).
So, it does uses the deleted id, if the server is restarted.
I got this issue because I had another table called user_rules which stored user_id in a column, but it did not have FK reference to user table (which was wrong). Another issue was that when user was getting deleted, entry from user_rules table was not getting deleted and since it had no FK set, db also didn't complain.
And when server got restarted, user rules were messed up!

Indexes in MySQL (Unique, Write Statement)

I Have 2 Question
If my table contain a unique column like this:
DROP TABLE IF EXISTS TestTable;
CREATE TABLE TestTable(
ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
GUID VARCHAR(32) UNIQUE NULL);
Do i Need to create an Index for this GUID column.
Note: i have GUID column In Where statement with join tables
My Second Question is the update statement will effect the index table if the updated column(s) not have been indexes?
No, UNIQUE is kind of index, so you don't need another index on the same column.
It won't update the index, if the changed column is not indexed.
Indexes that are not changed do not get updated.
source
It depends on which database you are using. Different databases have different ways of indexing.
If you are using InnoDB then the Primary Key and Unique Key is already an index, so you won't need to. If you create manually yet another index for the GUID column then you will have an extra redundant index on that column which wastes space.

Reset the row number count in SQLite3/MySQL

I am using SQLite3. I load a table with say 30 rows using integer as Primary ID and it auto-increments.
Now I delete all the rows from the table and then, reload some new information onto the table.
Problem is: the row count (my PrimaryID) now starts with 31. Is there any way that I can start loading new rows from the number 1 onwards?
SQLite
Use:
DELETE FROM your_table;
DELETE FROM sqlite_sequence WHERE name = 'your_table';
Documentation
SQLite keeps track of the largest ROWID that a table has ever held using the special SQLITE_SEQUENCE table. The SQLITE_SEQUENCE table is created and initialized automatically whenever a normal table that contains an AUTOINCREMENT column is created. The content of the SQLITE_SEQUENCE table can be modified using ordinary UPDATE, INSERT, and DELETE statements. But making modifications to this table will likely perturb the AUTOINCREMENT key generation algorithm. Make sure you know what you are doing before you undertake such changes.
Found the answer on SO: SQLite Reset Primary Key Field
MySQL
Use:
ALTER TABLE tbl AUTO_INCREMENT = 1;
In either case, the database doesn't care if the id numbers are sequencial - only that the values are unique. If users never see the primary key value (they shouldn't, because the data can change & won't always be at that primary key value), I wouldn't bother with these options.
For MySQL:
Use TRUNCATE TABLE tablename to empty the table (delete all records) and reset auto increment count.
You can also use ALTER TABLE tablename AUTO_INCREMENT = 0; if you just want to reset the count.
For SQLite:
DELETE FROM tablename;
DELETE FROM SQLITE_SEQUENCE WHERE name='tablename';
References
SQLite AutoIncrement
MySQL AutoIncrement
For SQLite use (not need to delete and create the table)
UPDATE SQLITE_SEQUENCE SET SEQ=0 WHERE NAME='table_name';
For MySql use
ALTER TABLE table_name AUTO_INCREMENT = 1;
You should not use AUTOINCREMENT in this case. Simply define your primary key as INTEGER PRIMARY KEY and the count will be reset to 1 after a DELETE FROM query. Without AUTOINCREMENT, the default behaviour will still be an automatic increment of the primary key as long as you don't run out of space in your table (in that case, old - deleted - values will be reused).
More information available in the SQLite Autoincrement document.
ALTER TABLE tbl AUTO_INCREMENT = 0;

In MySQL, how do I write a query to skip a duplicate row while inserting, when there's no unique field (primary key)?

I am trying to insert rows into a table that has no unique field or primary key. How can I write a query that will simply ignore the insert if there already exists a row with the exact same values on all fields -- a duplicate row?
Thanks.
You must have a primary key or unique key defined on some column or columns in the table for uniqueness to have any meaning. Every mechanism for detecting duplicates automatically relies on this being true.
You can't do the SELECT COUNT(*)... solution because it's subject to race conditions. That is, someone could insert a duplicate row in the moment after you select and before you insert. The only way around this is to lock the table with SELECT ... FOR UPDATE or LOCK TABLES.
Uh, why not make a primary key?
Otherwise, you have to basically do SELECT COUNT(*) FROM table WHERE field1=value AND ... AND fieldN=value for before EVERY insert.