Renumbering auto-increment values in rows in MySQL - mysql

I have a table with an auto-incrementing primary key. Because I allow my data to be deleted, there are now numerous gaps in the primary keys. For example, if I have row 1, 2 and 3, and 2 gets deleted, I only have rows 1 and 3 left (meaning the number 2 is vacant and empty).
This has proven to give me problems now that I am attempted to conduct a full data migration. So I'm wondering if it's possible at all to simply re-index everything - i.e. run some kind of MySQL UPDATE query such that I update all the rows' primary keys into a smooth, running order.
How do I go about doing this?

I think the easiest way is to create an empty clone of the table and copy all rows from your original one. That way all AUTO_INCREMENT fields are re-assigned.
1) Copy the table:
CREATE TABLE clone_table LIKE original_table;
2) Copy all rows, manually specifying all fields except your AUTO_INCREMENT one (id?) using the INSERT INTO ... SELECT ... syntax:
INSERT INTO clone_table (field1, field2, fieldN)
SELECT field1, field2, fieldN FROM original_table ORDER BY id;
3) Optionally, you can now delete old table and rename the clone:
DROP TABLE original_table;
RENAME TABLE clone_table TO original_table;

Related

MySQL renaming and create table at the same time

I need to rename MySQL table and create a new MySQL table at the same time.
There is critical live table with large number of records. master_table is always inserted records from scripts.
Need to backup the master table and create a another master table with same name at the same time.
General SQL is is like this.
RENAME TABLE master_table TO backup_table;
Create table master_table (id,value) values ('1','5000');
Is there a possibility to record missing data during the execution of above queries?
Any way to avoid missing record? Lock the master table, etc...
What I do is the following. It results in no downtime, no data loss, and nearly instantaneous execution.
CREATE TABLE mytable_new LIKE mytable;
...possibly update the AUTO_INCREMENT of the new table...
RENAME TABLE mytable TO mytable_old, mytable_new TO mytable;
By renaming both tables in one statement, they are swapped atomically. There is no chance for any data to be written "in between" while there is no table to receive the write. If you don't do this atomically, some writes may fail.
RENAME TABLE is virtually instantaneous, no matter how large the table. You don't have to wait for data to be copied.
If the table has an auto-increment primary key, I like to make sure the new table starts with an id value greater than the current id in the old table. Do this before swapping the table names.
SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='mydatabase' AND TABLE_NAME='mytable';
I like to add some comfortable margin to that value. You want to make sure that the id values inserted to the old table won't exceed the value you queried from INFORMATION_SCHEMA.
Change the new table to use this new value for its next auto-increment:
ALTER TABLE mytable_new AUTO_INCREMENT=<increased value>;
Then promptly execute the RENAME TABLE to swap them. As soon as new rows are inserted to the new, empty table, it will use id values starting with the increased auto-increment value, which should still be greater than the last id inserted into the old table, if you did these steps promptly.
Instead of renaming the master_backup table and recreating it, you could
just create a backup_table with the data from the master_table for the first backup run.
CREATE TABLE backup_table AS
SELECT * FROM master_table;
If you must add a primary key to the backup table then run this just once, that is for the first backup:
ALTER TABLE backup_table ADD CONSTRAINT pk_backup_table PRIMARY KEY(id);
For future backups do:
INSERT INTO backup_table
SELECT * FROM master_table;
Then you can delete all the data in the backup_table found in the master_table like:
DELETE FROM master_table A JOIN
backup_table B ON A.id=B.id;
Then you can add data to the master_table with this query:
INSERT INTO master_table (`value`) VALUES ('5000'); -- I assume the id field is auto_incrementable
I think this should work perfectly even without locking the master table, and with no missing executions.

Reset autoincrement value in mysql

I have table having serial no. 1,2,3,4,5 and so on
If I delete the data of for ex. 2 no. then my table shows 1, 3,4,5,6....
I want it should reset the serial no field. Can anybody help me in this?
Pravin
You can renumber the rows of your table by recreating it. That's probably the most foolproof way to do it if the table is small ... 100K rows or smaller.
Something like this would do the trick.
RENAME mytable TO mytable_previous;
CREATE mytable LIKE mytable_previous;
TRUNCATE TABLE mytable;
ALTER TABLE mytable AUTO_INCREMENT = 1;
INSERT INTO mytable
(col, col, col) /* name all cols except autoincrementing */
SELECT (col, col, col) /* same names, same order */
ORDER BY id;
This will allow the INSERT operation to redo the autoincrementing in the same order as the previous table, but without gaps.
BEWARE ... This is probably a bad idea. If the autoincrementing id column is used in other tables as a reference to this table, doing this will wreck your database. Production database tables that have DELETE operations ordinarily just live with gaps in the autoincremented row numbers. Those gaps certainly cause no harm.

How to get rid of duplicate results in a table

I have a table that has some duplicate results. For example:
`person_url` `movie_url`
1 2
1 2
2 3
Would become -->
`person_url` `movie_url`
1 2
2 3
I know how to do it by creating a new table,
create table tmp_credits (select distinct * from name);
However, it is a pretty large table and I have a couple indexes on it which will need to be re-created. How would I do this transformation in place, that is, without creating a new table?
You can add a UNIQUE index over your table's columns using the IGNORE keyword:
ALTER IGNORE TABLE name ADD UNIQUE INDEX (person_url, movie_url);
As stated in the manual:
IGNORE is a MySQL extension to standard SQL. It controls how ALTER TABLE works if there are duplicates on unique keys in the new table or if warnings occur when strict mode is enabled. If IGNORE is not specified, the copy is aborted and rolled back if duplicate-key errors occur. If IGNORE is specified, only the first row is used of rows with duplicates on a unique key. The other conflicting rows are deleted. Incorrect values are truncated to the closest matching acceptable value.
This will also prevent duplicates from being added in the future.
`create table temp
(col1 varchar(20),col2 varchar(20));
INSERT INTO temp VALUES
('1','one'),('2','two'),('2','two');
`select col1,col2 from temp
union
select col1,col2 from temp;
`
Have you considered just putting a semantic layer/view on top of the table that de-dups?
select person_url, movie_url
from name
group by person_url, movie_url

mySQL find dupes and remove them

I am wondering if there is a way to do this through one query.
Seems when I was initially populating my DB with dummy data to work with 10k records, somewhere in the mess of it all the script dummped an extra 1,044 rows where the rows are duplicates. I determined this using
SELECT x.ID, x.firstname FROM info x
INNER JOIN (SELECT ID FROM info
GROUP BY ID HAVING count(id) > 1) d ON x.ID = d.ID
What I am trying to figure out is through this single query can I add another piece to it that will remove one of the matching dupes from each dupe found?
also I realize the ID column should have been set to auto increment, but it wasn't
My favorite way of removing duplicates would be:
ALTER IGNORE TABLE info ADD UNIQUE (ID);
To explain a bit further (for reference, take a look here)
UNIQUE - you are adding unique index to ID column.
IGNORE - is a MySQL extension to standard SQL. It controls how ALTER TABLE works if there are duplicates on unique keys in the new table or if warnings occur when strict mode is enabled. If IGNORE is not specified, the copy is aborted and rolled back if duplicate-key errors occur. If IGNORE is specified, only the first row is used of rows with duplicates on a unique key. The other conflicting rows are deleted. Incorrect values are truncated to the closest matching acceptable value.
The query that I use is generally something like
Delete from table where id in (
Select Max(id) from table
Group by (DUPFIELD)
Having count (*)>1)
You have to run this several times since it all only remove one duplicated row at a time, but it's fast.
The most efficient way is you do it in below steps:
Step 1: Move the non duplicates (unique tuples) into a temporary table
CREATE TABLE new_table as
SELECT * FROM old_table WHERE 1 GROUP BY [column to remove duplicates by];
Step 2: delete delete the old table.We no longer need the table with all the duplicate entries, so drop it!
DROP TABLE old_table;
Step 3: rename the new_table to the name of the old_table
RENAME TABLE new_table TO old_table;

Deleting duplicate records in join table

I have a HABTM association between user and role.
User can be an admin (role_id = 1) or a user (role_id = 2) for roles.
In the join table, roles_users, I have some redundant records. For ex:
I want to remove the duplicate records such as 1:1, 2:4.
Two questions:
Where's the best place to execute the sql script that removes the dups -- migration? script?
What is the sql query to remove the dups?
CREATE TABLE roles_users2 LIKE roles_users; -- this ensures indexes are preserved
INSERT INTO roles_users2 SELECT DISTINCT * FROM roles_users;
DROP TABLE roles_users;
RENAME TABLE roles_users2 TO roles_users;
and for the future, to prevent duplicate rows
ALTER TABLE roles_users ADD UNIQUE INDEX (role_id, user_id);
Or, you can do all of it in one step with ALTER TABLE IGNORE:
ALTER IGNORE TABLE roles_users ADD UNIQUE INDEX (role_id, user_id);
IGNORE is a MySQL extension to standard SQL. It controls how ALTER TABLE works if there are duplicates on unique keys in the new table or if warnings occur when strict mode is enabled. If IGNORE is not specified, the copy is aborted and rolled back if duplicate-key errors occur. If IGNORE is specified, only the first row is used of rows with duplicates on a unique key. The other conflicting rows are deleted. Incorrect values are truncated to the closest matching acceptable value.
The simplest is to copy the data into a new table, minus the duplicates:
CREATE TABLE roles_users2 AS
SELECT DISTINCT * FROM roles_users
You can then choose one of the following:
Drop the old table, rename the new table to the old name and add indexes.
Truncate the old table and insert the rows from roles_users2 back into roles_users.