Innodb doesn't let me update the composite primary key? - mysql

I'm trying to create a history table to store all historical records of its corresponding table
However, when I switch from the MyISAM to InnoDB (because of the DELETE ON CASCADE) the below query yields the error: Incorrect table definition; there can be only one auto column and it must be defined as a key
CREATE TABLE tdm_history.BATCH LIKE tdm.BATCH;
ALTER TABLE tdm_history.BATCH MODIFY COLUMN id int NOT NULL,
DROP PRIMARY KEY, ADD action VARCHAR(8) DEFAULT 'insert',
ADD revision INT NOT NULL AUTO_INCREMENT,
ADD stamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
ADD PRIMARY KEY (id, revision);
I expect the primary of the tdm_history.BATCH from id to be changed to the composite primary key of (id, revision).
Note that, the above query works perfectly fine when the engine is set to MyISAM

Two guesses:
Plan A: Swap the MODIFY id and the DROP PK.
Plan B: Split into two ALTERs. But I don't know where. I think the problem is that ALTER has not quite let go of the id-PK link before you replace the PK. So, at least, move the ADD PK to a second ALTER.

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.

Is it good practice to add primary keys to a tables during the alter statement, or it makes no deference to add them when I cam creating the table

I created/defined an admin table, now I have seen other programmers alter the table and add keys to the tables
CREATE TABLE `admin` (
`admin_id` int(11) NOT NULL AUTO_INCREMENT,
`admin_name` varchar(255) NOT NULL,
`admin_surname` varchar(255) NOT NULL,
`phone` CHAR(10) NOT NULL,
`admin_email` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`admin_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `admin`
ADD PRIMARY KEY (`admin_id`),
ADD UNIQUE KEY `admin_email` (`admin_email`);
If I have already defined the table why should I alter the definition again here?
In InnoDB there exists clustered index always.
When primary key exists in a table then it is used as clustered index.
When no primary key but unique index(es) which's expression does not innclude NULLable columns exists then the most upper unique index from them in the table definition is clustered.
When no such unique index then inner hidden row number is used as an expression for clustered index.
Hence, if you create a table (and some expression is used for clustered index) and then use ALTER TABLE for to add primary key then the table must be rebuilt. It doesn't matter when the table is empty, but when there is data in it the process may be long enough (because COPY method is used).
If you create primary key as a part of CREATE TABLE then this is always fast.
I like to put all the index definitions inside the CREATE TABLE, and put them at the end instead of sitting on the column definitions.
Potential problem 1:
But I notice that some dump utilities like to add the indexes later. This may be a kludge to handle FOREIGN KEY definitions. Those have trouble if the tables are not created in just the right order.
It would seem better to simply ADD FOREIGN KEY... after all the tables are created and indexed.
Potential problem 2:
If you will be inserting a huge number of rows, it is usually more efficient to make the secondary keys after loading the data. This is more efficient than augmenting the indexes as it goes. For small tables (under, say, a million rows), this is not a big deal.
I do not understand why they ADD PRIMARY KEY after loading the data. That requires (as Akina points out) tossing the fabricated PK, sorting the data, and adding the real PK. That seems like extra work, even for a huge table.
If the rows are sorted in PK order, the loading is more efficient. The table is ordered by the PK (for InnoDB); inserting in that order is faster than jumping around. (mysqldump will necessarily provide them in PK order, so it is usually a non-issue.)

add unique constraints in column that related existing auto-inc primary key / partition key

summary
in mysql,
I want add uniqueness to existing column, without drop table, but it spits error because of existing auto-increment / primary key / partition key.
describe
I have table like below. and already have some rows.
CREATE TABLE my_table (
`id` INT NOT NULL AUTO_INCREMENT,
`event_tx_id` varchar(64) NOT NULL,
`amount` int(10),
PRIMARY KEY(id)
)
ENGINE=InnoDB
PARTITION BY KEY(id)
and I changed my mind, I want add constraint to column event_tx_id also unique.
I tried
when I try alter table like this (add unique),
ALTER TABLE my_table MONDIFY COLUMN event_tx_id varchar(64) UNIQUE;
it spits error that
Error Code: 1503. A UNIQUE INDEX must include all columns in the table's partitioning function 0.078 sec
so I tried partition by key, first, then
ALTER TABLE my_table PARTITION BY KEY(id, event_tx_id);
then spits
ALTER TABLE my_table PARTITION BY KEY(id, event_tx_id); Error Code: 1503. A PRIMARY KEY must include all columns in the table's partitioning function
tries above needs changing primary key. so when I try to change primary key (drop -> add, because there isn't modify method)
ALTER TABLE my_table DROP PRIMARY KEY Error Code: 1075. Incorrect table definition; there can be only one auto column and it must be defined as a key
it complains about YOU CANNOT CHANGE(DROP) PRIMARY KEY THAT HAS AUTO-INCREMENT..
is there any way to handle this .. ?
thanx.
Uniqueness constraints do not work on Partitioned tables, except where the partition key is part of the constraint.
Partitioning rarely provides any performance benefit, or any other benefit. What were you hoping to achieve?

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.

MySQL populate new column with incrementing values without auto_increment

I see lots of almost similar questions with obvious answers but I'm fairly sure this question isn't already on here.
I need to add an auto-incrementing id column to an existing table and set it as the primary key. I can't lose any of the existing data.
I can successfully make the change to the table structure but I get an error about truncated data in the new column. When I view the data every value in the new auto-incrementing column is null (and therefore not unique).
How can I back-fill these values to ensure uniqueness in my primary key?
***I would prefer to avoid dumping the existing data to a temporary table and re-inserting if there is a simpler solution.
Current script:
alter table the_table add new_field int first;
alter table the_table drop primary key, add primary key (new_field);
alter table the_table change new_field new_field int unsigned not null auto_increment;
I run the script in this order as I can't have an auto-incrementing column that isn't the primary key.
(MySQL 5.3)
Try creating the column, setting it as primary key and auto increment in one go
ALTER TABLE `the_table` ADD `new_field` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
Just add an auto-increment field, without being a primary key:
ALTER TABLE `the_table` ADD `new_field` INT NOT NULL AUTO_INCREMENT