Altering existing unique constraint - mysql

I have a table that was defined like this:
CREATE TABLE `Message` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`user_id` integer NOT NULL,
`user_to` integer NOT NULL,
`top_num` integer NOT NULL,
`priority` smallint NOT NULL,
`error` varchar(120) NOT NULL,
UNIQUE (`user_id`, `user_to`, `top_num`)
);
Later, I added another column to it, msg_type, like this:
ALTER TABLE Message ADD COLUMN msg_type SMALLINT(6) NOT NULL DEFAULT 0;
However, I have come to realize that I need to change my original UNIQUE constraint to include msg_type. I tried running
ALTER TABLE Message
ADD UNIQUE INDEX (`user_id`, `user_to`, `top_num`, `msg_type`);
but INSERTs into my table still fail, and the error message indicates that that is because the old uniqueness constraint fails.
When I call describe Messages in mysql I see the following:
+-----------------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | MUL | NULL | |
| user_to | int(11) | NO | MUL | NULL | |
| top_num | int(11) | NO | MUL | NULL | |
| priority | smallint(6) | NO | | NULL | |
| error | varchar(120) | NO | | NULL | |
| msg_type | smallint(6) | NO | | 0 | |
+-----------------+----------------------+------+-----+---------+----------------+
which makes it seem like msg_type really isn't part of the constraint... How can I alter the constraint that the table was defined with, short of recreating the table?

As in previous answer to change foreign key constraint use steps:
Step 1: Drop old constraint:
ALTER TABLE `Message` DROP INDEX `user_id`;
Step 2: Add new:
ALTER TABLE `Message` ADD UNIQUE INDEX (
`user_id`,
`user_to`,
`top_num`,
`msg_type`);
Use SHOW CREATE TABLE to know name of constraint:
mysql> SHOW CREATE TABLE `Message` ;
| Message | CREATE TABLE `Message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`user_to` int(11) NOT NULL,
`top_num` int(11) NOT NULL,
`priority` smallint(6) NOT NULL,
`error` varchar(120) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`,`user_to`,`top_num`)
-- ^^^^^^^^^ name
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
If you checks:
mysql> SHOW INDEX FROM `Message`;
Key_name is user_id that is first argument in UNIQUE (user_id ....
Suppose if you write:
ALTER TABLE `Message` ADD UNIQUE INDEX (
`user_to`,
`user_id`,
`top_num`,
`msg_type`);
Then you have to drop using user_to as:
ALTER TABLE `Message` DROP INDEX `user_to`;

This is because you are adding unique index. Please first drop unique index and then add unique constraint.
-- you have to drop each index one by one.
ALTER TABLE Message DROP UNIQUE INDEX user_id;
and now add unique constraint.
ALTER TABLE Message ADD CONSTRAINT uc_message UNIQUE (`user_id`, `user_to`, `top_num`, `msg_type`);

This is because you haven't deleted the first unique constraint you have created. Right now, you have two unique constraints on your table.
To delete a unique constraint, have a look at this post
Dropping Unique constraint from MySQL table

This is because you are adding unique index. Please first drop unique index and then add unique constraint.
DROP INDEX index_name ON table_name
and now add unique constraint.
ALTER TABLE Message
ADD CONSTRAINT uc_message UNIQUE ((`user_id`, `user_to`, `top_num`, `msg_type`);)

Related

How to add auto_increment field to a table with huge data?

Say there's a table table_test, and there're about 10M rows data in the table. And I want to add a auto_increment field like key to do some pagination work. What is the best way to make it work?
the table stucture as below:
# table_test
+---------------------------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------------------------+--------------+------+-----+---------+-------+
| ad_id | int(64) | NO | PRI | NULL | |
| account_id | varchar(64) | YES | MUL | NULL | |
| country | varchar(64) | NO | PRI | NULL | |
| image_hash | varchar(64) | YES | | NULL | |
+---------------------------------------+--------------+------+-----+---------+-------+
| table_test | CREATE TABLE `table_test` (
`ad_id` int(11) NOT NULL,
`account_id` varchar(64) DEFAULT NULL,
`country` varchar(64) NOT NULL,
`image_hash` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`,`country`),
KEY `account_id` (`account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
update:
change id to ad_id, and ad_id here is unique key togather with field country, so ad_id itself may be duplicated.
I'd go:
-- drop PK as AUTO increment demands being the PK
ALTER TABLE table_test DROP PRIMARY KEY;
-- add Id as PK and the first column
ALTER TABLE table_test ADD Id INT AUTO_INCREMENT PRIMARY KEY FIRST;
-- make sure the old PK is unique
ALTER TABLE table_test ADD CONSTRAINT table_test_uk UNIQUE (ad_id, country);
or perhaps:
CREATE TABLE table_test2 LIKE table_test;
ALTER TABLE table_test2 DROP PRIMARY KEY;
-- add Id as PK and the first column
ALTER TABLE table_test2 ADD Id INT AUTO_INCREMENT PRIMARY KEY FIRST;
-- make sure the old PK is unique
ALTER TABLE table_test2 ADD CONSTRAINT table_test2_uk UNIQUE (ad_id, country);
INSERT INTO table_test2(ad_id, account_id, country, image_hash)
SELECT ad_id, account_id, country, image_hash FROM table_test;
DROP TABLE table_test;
RENAME TABLE table_test2 TO table_test;
Just Alter your table
ALTER TABLE table_test CHANGE id id INT(11) NOT NULL AUTO_INCREMENT;
You can already use the 'id' column as an auto increment (unless you are using UUID or some other strategy).
You can check the following answer on how to alter the table to add auto increment to a table:
ALTER table - adding AUTOINCREMENT in MySQL
Edit:
If you wish to add a new column that will be auto incremented , you can use the following:
ALTER TABLE table_test ADD id INT AUTO_INCREMENT;

how to set a column to be a primary key?

I have this table in mysql database
number | username | friendname | location
------------------------------------------------------------
1 | Nifa salem |jack | 47.117828 -88.545625
2 | Flora | fred | 38.898556 -77.037852
3 | Flora | Nifa salem | 32.9697 -96.80322
4 | Flora | Anne | 29.46786, -98.53506
but it says this " This table does not contain a unique column. Grid edit, checkbox, Edit, Copy and Delete features are not available."
Now i need to set the number column to be the priamry key column! how can o do it! as i need to edit the data in this table.
You can do this when creating the table ...
CREATE TABLE `admin` (
`id` int(8) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL DEFAULT '',
`friendname` varchar(64) NOT NULL DEFAULT '',
`locatino` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Is this not as simple as
ALTER TABLE yourTableName
ADD PRIMARY KEY (number);
ALTER TABLE TableName ADD PRIMARY KEY(Number);

Mysql Slow Query - Even with all the indices

mysql> explain
select c.userEmail,f.customerId
from comments c
inner join flows f
on (f.id = c.typeId)
inner join users u
on (u.email = c.userEmail)
where c.addTime >= 1372617000
and c.addTime <= 1374776940
and c.type = 'flow'
and c.automated = 0;
+----+-------------+-------+--------+----------------------------------------+------------+---------+---------------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+----------------------------------------+------------+---------+---------------------+--------+-------------+
| 1 | SIMPLE | f | index | PRIMARY | customerId | 4 | NULL | 144443 | Using index |
| 1 | SIMPLE | c | ref | userEmail_idx,addTime,automated,typeId | typeId | 198 | f.id,const | 1 | Using where |
| 1 | SIMPLE | u | eq_ref | email | email | 386 | c.userEmail | 1 | Using index |
+----+-------------+-------+--------+----------------------------------------+------------+---------+---------------------+--------+-------------+
How do I make the above query faster - it constantly shows up in the slow query logs.
Indexes present :
id is the auto incremented primary key of the flows table.
customerId of flows table.
userEmail of comments table.
composite index (typeId,type) on comments table.
email of users table (unique)
automated of comments table.
addTime of comments table.
Number of rows :
1. flows - 150k
2. comments - 500k (half of them have automated = 1 and others have automated = 0) (also value of type is 'flow' for all the rows except 500)
3. users - 50
Table schemas :
users | CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(128) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=56 DEFAULT CHARSET=utf8
comments | CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userEmail` varchar(128) DEFAULT NULL,
`content` mediumtext NOT NULL,
`addTime` int(11) NOT NULL,
`typeId` int(11) NOT NULL,
`automated` tinyint(4) NOT NULL,
`type` varchar(64) NOT NULL,
PRIMARY KEY (`id`),
KEY `userEmail_idx` (`userEmail`),
KEY `addTime` (`addTime`),
KEY `automated` (`automated`),
KEY `typeId` (`typeId`,`type`)
) ENGINE=InnoDB AUTO_INCREMENT=572410 DEFAULT CHARSET=utf8 |
flows | CREATE TABLE `flows` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(32) NOT NULL,
`status` varchar(128) NOT NULL,
`customerId` int(11) NOT NULL,
`createTime` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `flowType_idx` (`type`),
KEY `customerId` (`customerId`),
KEY `status` (`status`),
KEY `createTime` (`createTime`),
) ENGINE=InnoDB AUTO_INCREMENT=134127 DEFAULT CHARSET=utf8 |
You have the required indexes to perform the joins efficiently. However, it looks like MySQL is joining the tables in a less efficient manner. The EXPLAIN output shows that it is doing a full index scan of the flows table then joining the comments table.
It will probably be more efficient to read the comments table first before joining. That is, in the order you have specified in your query so that the comment set is restricted by the predicates you have supplied (probably what you intended).
Running OPTIMISE TABLE or ANALYZE TABLE can improve the decision that the query optimiser makes. Particularly on tables that have had extensive changes.
If the query optimiser still gets it wrong you can force tables to be read in the order you specify in the query by beginning your statement with SELECT STRAIGHT_JOIN or by changing the INNER JOIN to STRAIGHT_JOIN.

MySQL inserting rows with 0 value in primary key

MySQL (v5.41) on Ubuntu is inserting rows with primary key value as 0.
Below is the MySQL table data.
mysql> select id from keywords where text_id = 72;
+----+
| id |
+----+
| 0 |
| 0 |
+----+
| keywords | CREATE TABLE `keywords` (
`id` int(11) NOT NULL DEFAULT '0',
`to_user_id` bigint(20) DEFAULT NULL,
`text_id` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 |
+--------+---------------------------------
set the primary key column to AUTO INCREMENT also.
Like
CREATE TABLE table (
id INT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;
You probably don't have the field id as AUTOINCREMENT

MySQL won't use index for query?

I have this table:
CREATE TABLE `point` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`siteid` INT(11) NOT NULL,
`lft` INT(11) DEFAULT NULL,
`rgt` INT(11) DEFAULT NULL,
`level` SMALLINT(6) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `point_siteid_site_id` (`siteid`),
CONSTRAINT `point_siteid_site_id` FOREIGN KEY (`siteid`) REFERENCES `site` (`id`) ON DELETE CASCADE
) ENGINE=INNODB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
And this query:
SELECT * FROM `point` WHERE siteid = 1;
Which results in this EXPLAIN information:
+----+-------------+-------+------+----------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------+------+---------+------+------+-------------+
| 1 | SIMPLE | point | ALL | point_siteid_site_id | NULL | NULL | NULL | 6 | Using where |
+----+-------------+-------+------+----------------------+------+---------+------+------+-------------+
Question is, why isn't the query using the point_siteid_site_id index?
I haven't used MySQL much, but in PostgreSQL if the number of records is small in the table, it can decide not to use the index. This is not a problem, because it chooses the best query plan for the situation. When the number of records is bigger, it will use the index. Maybe this is the same case here with MySQL.
Edit: Are you sure the foreign key is indexed?
It's probably because you don't have enough sample data, or the cardinality (diversity) of the siteid values isn't very large. These are the most common reasons the optimizer will find it quicker just to read all the values.