MySQL moving primary key from varchar to int - mysql

I have three tables in MySQL (innodb) (X, Y and Z). X is a table with more than 10 million rows, and has primary key of Y as foreign key. Similarly, Z is table with more than 30 million rows and has primary key of Y as foreign key.
Now the problem is that primary key of Y is VARCHAR (something like a md5 hash or GUID). I want to move this key to INT (AUTO_INCREMENT). What is a way to achieve this in mysql, without writing a script in any other language?
Additionally, primary key of table Z is also a VARCHAR (md5/GUID). I would like to change that to integer as well. (It's not a foreign key in any table).

(This may or may not be any better than Ritobroto's suggestion.)
Assuming X links to Y. (Adapt as needed for all the FKs.)
Do something like this for each table.
ALTER TABLE X
DROP FOREIGN KEY ..., -- We'll add it back later
ADD id INT UNSIGNED NOT NULL AUTO_INCREMENT, -- Replacement PK
DROP PRIMARY KEY, -- Assuming it was `guid`
ADD PRIMARY KEY(id),
ADD INDEX(X_guid), -- Lost the FK; still (for now) need an index
ADD COLUMN Y_id INT UNSIGNED NOT NULL -- future FK to Y
;
Get the new ids linked up (to replace the guids). For each link:
UPDATE X JOIN Y ON X.Y_guid = Y.guid
SET x.y_id = y.id;
(This will take a long time.
Re-establish the FKs. For each table:
ALTER TABLE ...
ADD FOREIGN KEY ..., -- to tie `id` instead of `guid`
DROP INDEX(X_guid); -- unless you need it for something else
Practice it on a test machine !!

Step 1
Create tables with same column names but the datatype of primary key of Y should be in INT AUTO INCREMENT,Same goes for table Z.
Step 2
Use this query:
INSERT INTO table_name (column_names of present table except the primary key column_name) SELECT all the columns except the primary key column FROM Y/Z(which ever table you want from the data to be inserted).
Then you can drop the original table.
It's a painful process for such amount of data,but will do what you have wanted.

Related

Differences between defining primary key - along with column name, at the end of create table stmt, adding primary key index after create table stmt

There are three ways I have seen to define primary keys.
Define along with its column name definition:
CREATE TABLE test (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-- other fields
);
Define the key at the end of the table definition:
CREATE TABLE test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
-- other fields
PRIMARY KEY (id)
);
Adding primary key index after table creation. Generally I have seen this in phpMyAdmin's exported .sql files. (Does it depends on the storage engine used?)
CREATE TABLE test (
id INT UNSIGNED NOT NULL,
-- other fields
);
ALTER TABLE test
ADD PRIMARY KEY (id),
MODIFY id INT UNSIGNED NOT NULL AUTO_INCREMENT;
What are the internal differences between all these methods?
Mostly I have seen that importing an SQL file having the 3rd method takes longer time than having other methods.
Edit (After Bill Karwin told that "(the) example(s) shows no import of data"):
The examples above don't contain INSERT queries, but what differences there will be if there are INSERT statements after each of these CREATE TABLE queries for inserting data in them?
There is no difference between the first two forms. It's only a syntax convenience if your primary key is a single column. But if you have a multi-column primary key, you must define the PK as a table constraint:
CREATE TABLE test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
other INT NOT NULL,
-- other fields
PRIMARY KEY (id, other)
);
The third form is almost the same, because you define the primary key before inserting any data into the table. The only effect is that metadata is altered by the second DDL statement.
Some people claim that adding the primary key after importing data is faster, but this is not true for MySQL's default storage engine InnoDB. The table data is stored as a clustered index. If you don't declare your own primary key, another row id is created implicitly, and this becomes the key for the clustered index. So you're inserting into an index one way or the other.
It's possible that in the old MyISAM storage engine, inserting data to a table with no primary key is a little faster. But you have to count the extra time it takes to add the primary key after you're done inserting data.
In any case, your example shows no import of data, so it's moot.

MySQL autoincrement value range

Scenario is that I have 2 tables of the same structure, however I only want to allow php permissions to update table B, while table A can only be updated via DBMS.
These 2 tables are merged into a single php array, so I would like to set primary key ranges to seperate them at this point to avoid conflict of primary key (a simple autoincrement integer for best indexing).
As far as I know the simplest would be to constrain table A to have primary key auto increment values from 1000000 to 1999999 and then table B 2000000 upwards.
Is this possible to constrain min-max autoincrement values (I know I can start them at a given integer so asking if there is a simple 'max' to put on table A).
This simple configuration would ensure integrity.
Would an 'after_insert' type trigger work to remove the new row and throw an SQL error ?
You could create one table with id as mediumint (max 8 388 607 or twice as much for unsigned):
create table tableA( id mediumint(5) not null auto_increment, `test` varchar(5), primary key (id)) ;
and second with int and auto_increment value set over mediumint max:
create table tableB( id int(5) not null auto_increment, `test` varchar(5), primary key (id)) auto_increment=8388608 ;
https://dev.mysql.com/doc/refman/8.0/en/integer-types.html
But i think that much more elegant would be to utilize auto_increment_increment mechanism.
auto-increment-increment = 2 //global for all tables in mysql.ini
SET ##auto_increment_increment=2; //run-time just for one session
Set in tableA first auto_increment=1 and in tableB auto_increment=2 and You will never collide. One table will have odd ids and second will have even ids. This way You do not have to worry about reaching id limit.

The best way for designing many to many entities relationship

I have two tables permissions and groups of many to many relationship
CREATE TABLE `permissions` (
`Permission_Id` int(11) NOT NULL AUTO_INCREMENT,
`Permission_Name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`Permission_Id`)
)
Groups table
CREATE TABLE `groups` (
`Group_Id` int(11) NOT NULL AUTO_INCREMENT,
`Group_Desc` varchar(100) DEFAULT NULL,
PRIMARY KEY (`Group_Id`)
)
I am confuse how to implement the many to many relationship
which is better to create a composite primary key of Group_id and Permission_id in a new table
Or to create a new table & select the columns from the two table using join keyword .
From my blog:
Do it this way.
CREATE TABLE XtoY (
# No surrogate id for this table
x_id MEDIUMINT UNSIGNED NOT NULL, -- For JOINing to one table
y_id MEDIUMINT UNSIGNED NOT NULL, -- For JOINing to the other table
# Include other fields specific to the 'relation'
PRIMARY KEY(x_id, y_id), -- When starting with X
INDEX (y_id, x_id) -- When starting with Y
) ENGINE=InnoDB;
Notes:
⚈ Lack of an AUTO_INCREMENT id for this table -- The PK given is the 'natural' PK; there is no good reason for a surrogate.
⚈ "MEDIUMINT" -- This is a reminder that all INTs should be made as small as is safe (smaller ⇒ faster). Of course the declaration here must match the definition in the table being linked to.
⚈ "UNSIGNED" -- Nearly all INTs may as well be declared non-negative
⚈ "NOT NULL" -- Well, that's true, isn't it?
⚈ "InnoDB" -- More effecient than MyISAM because of the way the PRIMARY KEY is clustered with the data in InnoDB.
⚈ "INDEX(y_id, x_id)" -- The PRIMARY KEY makes it efficient to go one direction; this index makes the other direction efficient. No need to say UNIQUE; that would be extra effort on INSERTs.
⚈ In the secondary index, saying just INDEX(y_id) would work because it would implicit include x_id. But I would rather make it more obvious that I am hoping for a 'covering' index.
To conditionally INSERT new links, use IODKU
Note that if you had an AUTO_INCREMENT in this table, IODKU would "burn" ids quite rapidly.
More
A FOREIGN KEY implicitly creates an index on the column(s) involved.
PRIMARY KEY(a,b) (1) says that the combo (a,b) is UNIQUE, and (2) orders the data by (a,b).
INDEX(a), INDEX(b) (whether generated by FOREIGN KEY or generated manually) is not the same as INDEX(a,b).
InnoDB really needs a PRIMARY KEY, so you may as well say PRIMARY KEY (a,b) instead of UNIQUE(a,b).
I know the solution.
I need a to create "junction" table to hold many-to-many relationship in this case.
CREATE TABLE Groups_Permissions
(
Group_Id INT,
Permission_Id INT,
)
The combination of Group_Id and Permmission_Id should be UNIQUE and have FK to groups and permission tables.

SQL: Create table with two indexes

I previously created a table using the following SQL code:
CREATE TABLE myTable (
id_A BIGINT NOT NULL,
id_B INT NOT NULL,
some_info varchar(255),
some_info2 varchar(255),
PRIMARY KEY (id_A)
)
In the previous table created, both id_A and id_B would be unique values. I understand that id_A is forced to be unique by the
PRIMARY KEY (id_A) code, which is perfect (which also indexes it), however id_B isn't
Here is where I am confused. id_B will also be unique, however I am not sure how to force it to be unique in the table,and how to make the database create an index for it so that future queries that use SELECT on this table will have good preformance. I know I can't have two PRIMARY KEYS:
PRIMARY KEY (id_A)
PRIMARY KEY (id_B)
How might I go about making an index for id_B as well so that future queries happen efficiently?
You can add a UNIQUE INDEX on the column. While a table can only have one PRIMARY KEY, it can have as many UNIQUE INDEXes as you might need.
ALTER TABLE myTable
ADD UNIQUE INDEX `UI_myTable_idB` (`id_B` );
You can use the MySQL UNIQUE KEY for the column. This will force the values in the id_B column to be unique, but not use it as the primary key column. You can achieve it with this statement:
CREATE TABLE myTable (
id_A BIGINT NOT NULL,
id_B INT NOT NULL,
some_info varchar(255),
some_info2 varchar(255),
PRIMARY KEY (id_A),
UNIQUE KEY (id_b))
This will automatically index the column like you want it to. Primary keys and unique keys in a MySQL table are essentially the same, except a primary key cannot be NULL, and there can be only one primary key column (or combination of primary key columns). There can be any number of unique key columns.

Assign MySQL #rowid as value

I have an existing table with lot of rows (around 10k rows) with two columns as primary keys as it is acting as middle table of many-to-many relation between two other table.
For new requirements, I need to assign add new column (say id) which must be primary key with auto increment values. I ran following queries:
ALTER TABLE `momento_distribution` ADD `id` INT( 11 ) NOT NULL FIRST;
ALTER TABLE `momento_distribution` DROP PRIMARY KEY , ADD PRIMARY KEY ( `id` );
First query run successfully but second query generated following error:
1062 - Duplicate entry '0' for key 'PRIMARY'
Reason is obvious, new column id got 0 as default value and Primary key can't have duplicate values.
Now before I can run second query, I need to set incremental value for new column like 1,2,3...
In Oracle, I know, this can be done through rowid. MySQL also have its equivalent #rowid. Can someone please suggest a query to set #rowid as column value for column id?
Please Note: This had to be done through query as I can't change 10000 rows manually.
You need to set it to AUTO_INCREMENT at the same time, that will populate it;
ALTER TABLE momento_distribution
ADD id INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
Demo here.
EDIT: If you have an existing primary key, you'll need to drop that at the same time;
ALTER TABLE momento_distribution
DROP PRIMARY KEY,
ADD id INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
Same question asked by same user differently. Refer to that question.
MySQL 1062 - Duplicate entry '0' for key 'PRIMARY'
In short,
1. Remove existing FK
2. Remove existing PK
3. Run your first query as
ALTER TABLE `momento_distribution` ADD `id` INT( 11 ) PRIMARY KEY AUTO_INCREMENT NOT NULL FIRST;
which will also assign unique number without depending on #rowid
4. Add FK to earlier columns, if needed.