Altering collation on field causes UNIQUE constraint to fail - mysql

When trying to alter my table with:
ALTER TABLE segment_item
CHANGE value value VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL;
I'm running into a UNIQUE constraint violation:
MySQLIntegrityConstraintViolationException: Duplicate entry for key 'segment_id'
Why would this be? This alteration to the table isn't adding a new record, it's just changing the character set and collation of existing records. For full visibility, here's the structure of the table:
CREATE TABLE `segment_item` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`value` varchar(100) COLLATE latin1_bin DEFAULT NULL,
`segment_id` binary(16) DEFAULT NULL,
`item_order` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `segment_id` (`segment_id`,`value`),
KEY `value` (`value`),
CONSTRAINT `segment_item_ibfk_1` FOREIGN KEY (`segment_id`) REFERENCES `segment` (`segment_id`)
) ENGINE=InnoDB AUTO_INCREMENT=36484 DEFAULT CHARSET=latin1 COLLATE=latin1_bin

The collation is how the db engine determines when records are unique. Looks like here you're changing from a binary collation (literally unique if and only if the bytes of the string are unique) to case insensitive unicode (which is case insensitive, but also uses a list of standard transformations to normalize digraphs, code point orders, etc).
So, for example, the following two strings used to be unique before your change (é is valid Latin-1):
Beyoncé
beyonce
But after the change they would be considered identical. My guess is that you have at least two "value"... er... values that are no longer unique under the new collation.

Related

Foreign key rejected despite columns having different data types

I'm trying to set up two tables in a database, and add a foreign key between them. They're declared as follows:
CREATE TABLE `clothing` (
`name` varchar(26) COLLATE utf8_bin NOT NULL,
`image` varchar(64) COLLATE utf8_bin NOT NULL,
`localized_name` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`localized_name`)),
`main` varchar(18) COLLATE utf8_bin DEFAULT NULL,
`stars` tinyint(3) unsigned NOT NULL,
`id` tinyint(3) unsigned NOT NULL,
`splatnet` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`splatnet`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `abilities` (
`name` varchar(18) COLLATE utf8_bin DEFAULT NULL,
`image` varchar(48) COLLATE utf8_bin NOT NULL,
`id` tinyint(3) unsigned NOT NULL,
`localized_name` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`localized_name`))
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
I want to create a foreign key on clothing that references abilities with the following command:
ALTER TABLE `abilities` ADD FOREIGN KEY (`name`) REFERENCES `clothing` (`main`);
However, attempting to do this raises this error in return:
Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.
Can't create table `prismarine_rusted`.`abilities` (errno: 150 "Foreign key constraint is incorrectly formed")
I'm not entirely sure what's causing this, and unless I'm overlooking something really obvious, main and name have the same type, and therefore, should be able to be tied together via a foreign key. I'm using MariaDB v10.4.12, with SQL mode set to TRADITIONAL.
Although the foreign and primary key columns involved here are the same type, you are trying to reference clothing.main, which is not a unique or primary key column. From the MariaDB documentation:
The referenced columns must be a PRIMARY KEY or a UNIQUE index.
Note that this differs from InnoDB on MySQL, where a foreign key column can in fact reference a non unique column in another table.
One way to remedy this error would be to make clothing.main a unique column:
ALTER TABLE clothing ADD UNIQUE (main);
Note that doing this might only make logical sense if the values in main are already unique. If not, then perhaps you would have to revisit your data model.
It might be because there is a value in abilities.name that has no match in the referenced table.

MemSQL - Surrogate key as Primary and different unique keys at the same time in table creation

I have a situation that I need to have a surrogate key (id) in place of a composite key (4 field combined to be unique: project_id, dataset_id, table_id, view_name) to easily refer that in other tables.
So to do this I used id field as Primary key and other 4 fields mentioned above as unique keys. This is allowed in MySQL but not in MemSQL.
Error Code: 1895. The unique key named: 'project_id' must contain all columns specified in the primary key when no shard key is declared
So I added the id field as the Shard key but no use.
CREATE TABLE `table_access_details` (
`id` integer primary key,
`project_id` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`dataset_id` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`table_id` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`view_name` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL ,
`upload_id` decimal (14,0) DEFAULT NULL,
`modified_datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
#SHARD KEY (`id`),
unique(`project_id`,`dataset_id`,`table_id`,`view_name`)
);
How can I overcome this situation in MemSQL?
So, you want unique key (id) as well as unique key (project_id, dataset_id, table_id, view_name)? This is not possible in a sharded table in memsql - the unique key cannot be efficiently enforced across shards. Your options are: don't use both unique keys, or make the table a reference table.

How do I solve the MySQL error where I Cannot add foreign key constraint because of ERROR 1215?

This is my first table.
CREATE TABLE `raw_orders` (
`row_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`order_id` VARCHAR(45) COLLATE utf8mb4_unicode_ci NOT NULL,
`order_revenue` FLOAT NOT NULL,
PRIMARY KEY(`row_id`),
KEY(`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
This is my second table
CREATE TABLE `formatted_orders` (
`order_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`client_order_id` VARCHAR(50) COLLATE utf8mb4_general_ci NOT NULL,
`order_revenue` FLOAT NOT NULL,
PRIMARY KEY(`order_id`),
KEY(`client_order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
I am trying to add foreign key in formatted_orders linking it to raw_orders by using this
ALTER TABLE formatted_orders
ADD FOREIGN KEY (client_order_id) REFERENCES raw_orders(order_id);
But I get this error
ERROR (HY000): Cannot add foreign key constraint
You can simply add a foreign key in the table formatted_orders like this:
CREATE TABLE `formatted_orders` (
`order_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`client_order_id` VARCHAR(50) COLLATE utf8mb4_general_ci NOT NULL,
`order_revenue` FLOAT NOT NULL,
PRIMARY KEY(`order_id`),
FOREIGN KEY (`client_order`) REFERENCES raw_orders(`order_id`)
)
The reason you cannot add the constraint is because you specify different collations for the columns in the two tables. Also, the columns should be the same size, although MySQL will let you create the constraint even if they're not.
Change to the same collation (COLLATE utf8mb4_unicode_ci for instance) for both columns and it will work.
See this SQL Fiddle for an example.
The MySQL documentation states that:
Corresponding columns in the foreign key and the referenced key must
have similar data types. The size and sign of integer types must be
the same. The length of string types need not be the same. For
nonbinary (character) string columns, the character set and collation
must be the same.

mysql 5.6 foreign key constraint error; didn't occur in 5.5

Tables involved:
phppos_permissions_actions:
mysql> show create table phppos_permissions_actions;
+----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| phppos_permissions_actions | CREATE TABLE `phppos_permissions_actions` (
`module_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`person_id` int(11) NOT NULL,
`action_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`module_id`,`person_id`,`action_id`),
KEY `phppos_permissions_actions_ibfk_2` (`person_id`),
KEY `phppos_permissions_actions_ibfk_3` (`action_id`),
CONSTRAINT `phppos_permissions_actions_ibfk_1` FOREIGN KEY (`module_id`) REFERENCES `phppos_modules` (`module_id`),
CONSTRAINT `phppos_permissions_actions_ibfk_2` FOREIGN KEY (`person_id`) REFERENCES `phppos_employees` (`person_id`),
CONSTRAINT `phppos_permissions_actions_ibfk_3` FOREIGN KEY (`action_id`) REFERENCES `phppos_modules_actions` (`action_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
phppos_modules
mysql> show create table phppos_modules;
+----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| phppos_modules | CREATE TABLE `phppos_modules` (
`name_lang_key` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`desc_lang_key` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`sort` int(10) NOT NULL,
`module_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`module_id`),
UNIQUE KEY `desc_lang_key` (`desc_lang_key`),
UNIQUE KEY `name_lang_key` (`name_lang_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Query:
ALTER TABLE `phppos_permissions_actions` CHANGE `module_id` `module_id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, CHANGE `action_id` `action_id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
Error:
#1832 - Cannot change column 'module_id': used in a foreign key constraint 'phppos_permissions_actions_ibfk_1'
(in mysql 5.5 there was no error)
The only way I can get it to work is by doing:
SET foreign_key_checks = 0;
What changed in 5.6? Is this a bug?
This is apparently an improvement in MySQL 5.6, though the implementation seems a little too strict.
http://dev.mysql.com/doc/refman/5.6/en/alter-table.html says:
As of 5.6.7, the server prohibits changes to foreign key columns with the potential to cause loss of referential integrity. A workaround is to use ALTER TABLE ... DROP FOREIGN KEY before changing the column definition and ALTER TABLE ... ADD FOREIGN KEY afterward.
The release notes say this is related to http://bugs.mysql.com/bug.php?id=46599
That's fine, however...
http://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html says:
Corresponding columns in the foreign key and the referenced key must have similar data types. The size and sign of integer types must be the same. The length of string types need not be the same. For nonbinary (character) string columns, the character set and collation must be the same.
Re your comment:
It's not really a contradiction. You can still create foreign keys in MySQL 5.6 with different string lengths.
create table foo ( p varchar(20) primary key );
create table bar ( f varchar(10), foreign key (f) references foo (p) );
You can modify columns as long as there's no chance of truncating data.
alter table bar modify column f varchar(20); /* increasing string length */
Query OK
But you can't modify columns if it might lose data.
alter table bar modify column f varchar(5); /* decreasing string length */
ERROR 1832 (HY000): Cannot change column 'f':
used in a foreign key constraint 'bar_ibfk_1'
And as you discovered, you can disable foreign key checks either with foreign_check_checks=0 or else by dropping the constraint with ALTER TABLE and then recreate the constraint after you modify your column.

(Bug?) InnoDB MySQL error 1025, errno 150 Foreign Key

I have a table whose primary key I'm trying to change.
this is the table definition.
CREATE TABLE `tbl_customer` (
`PersonId` int(11) NOT NULL,
`Id` int(10) unsigned NOT NULL,
`Name` varchar(100) collate utf8_spanish_ci NOT NULL,
`Alias` varchar(50) collate utf8_spanish_ci NOT NULL,
`Phone` varchar(30) collate utf8_spanish_ci default NULL,
`Phone2` varchar(30) collate utf8_spanish_ci default NULL,
`Email` varchar(50) collate utf8_spanish_ci default NULL,
`Email2` varchar(50) collate utf8_spanish_ci default NULL,
`RFC` varchar(13) collate utf8_spanish_ci default NULL,
`AddressStreetName` varchar(45) collate utf8_spanish_ci default NULL,
`AddressStreetNumber` varchar(45) collate utf8_spanish_ci default NULL,
`AddressCityWard` varchar(45) collate utf8_spanish_ci default NULL,
`AddressCityName` varchar(45) collate utf8_spanish_ci default NULL,
`AddressStateName` varchar(45) collate utf8_spanish_ci default NULL,
`AddressCountryName` varchar(45) collate utf8_spanish_ci default NULL,
`AddressPostalCode` int(10) default NULL,
`IsDistributor` tinyint(1) NOT NULL default '0' COMMENT '1 = Is Distributor, 0 = Is Not Distributor',
`ParentCustomerId` int(10) NOT NULL default '11' COMMENT 'Our Id is 11, so by default, all customers right now are our children.',
PRIMARY KEY (`Id`),
KEY `fk_tbl_cliente_tbl_cliente1_idx` (`ParentCustomerId`),
KEY `fk_tbl_cliente_tbl_person1_idx` (`PersonId`),
KEY `PersonId` (`PersonId`),
KEY `PersonId_2` (`PersonId`),
CONSTRAINT `fk_tbl_cliente_tbl_cliente1` FOREIGN KEY (`ParentCustomerId`) REFERENCES `tbl_customer` (`PersonId`),
CONSTRAINT `fk_tbl_cliente_tbl_person1` FOREIGN KEY (`PersonId`) REFERENCES `zapata`.`tbl_person` (`Id`) ON DELETE NO ACTION ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci COMMENT='''Customer'' refers to a person or entity to which we provide '$$
Now, when I first tried to:
ALTER TABLE `tbl_customer` DROP PRIMARY KEY;
My PRIMARY KEY is Id . When I tried to drop it I got..
Error Code: 1025. Error on rename of './services/#sql-29a_218cc7f' to './services/tbl_customer' (errno: 150)
So, I deleted all FOREIGN KEY constraints that referred to this table and column, and still got the same error. I also went over to SHOW ENGINE INNODB STATUS And found out this:
------------------------
LATEST FOREIGN KEY ERROR
------------------------
130226 14:41:11 Error in foreign key constraint of table services/tbl_employee_shift:
there is no index in referenced table which would contain
the columns as the first columns, or the data types in the
referenced table do not match to the ones in table. Constraint:
,
CONSTRAINT fk_tbl_employee_shift_tbl_customer1 FOREIGN KEY (CustomerId) REFERENCES services.tbl_customer (Id) ON UPDATE CASCADE
However, the table services.tbl_employee_shift does not exist (it existed once but it was dropped several weeks before I tried this change). So I went on and...
CREATE TABLE services.tbl_employee_shift(
CustomerId INT (11)
);
ALTER TABLE services.tbl_employee_shift ADD CONSTRAINT fk_tbl_employee_shift_tbl_customer1 FOREIGN KEY (CustomerId) REFERENCES avatar.tbl_cliente (Id);
ALTER TABLE services.tbl_employee_shift DROP FOREIGN KEY fk_tbl_employee_shift_tbl_customer1;
And it works... but it doesn't correct the necessary information, seemingly InnoDB still believes that the constraint fk_tbl_employee_shift_tbl_customer1 is alive and thus, is 'preventing the drop of the primary key to keep consistency'...
I'm using MySQL 5.0.95.
EDIT: This problem went unresolved, it was worked around
The problem could only be corrected when we migrated the database to a newer server (same mysql version), seems like there was a broken/ghost reference to a ghost foreign key (fk_tbl_employee_shift_tbl_customer1 ) which prevented the column from being dropped. Since this broken/ghostfk wasn't in the new server, I could drop the column with no problems then. My guess is it was a bug, but unfortunately I can't recreate it.
It sounds as though you dropped tbl_employee_shift whilst foreign_key_checks was set to 0:
Setting foreign_key_checks to 0 also affects data definition statements: DROP SCHEMA drops a schema even if it contains tables that have foreign keys that are referred to by tables outside the schema, and DROP TABLE drops tables that have foreign keys that are referred to by other tables.
Since this behaviour is documented, it must be considered by-design and therefore not a bug.
The error occurs when foreign key is bad formulated.
the SQL is correct, i run script in my localhost, i get same error.
the solution is verify that table tbl_person was created with engine "InnoDB".
greetings
Just dealt with this problem today. There was no sign of the two affected tables in any of the schema information tables. I searched the various system databases looking for the FK, to no avail. In the end, dropping the database worked instantly.
I had a similar error, what mysql requires is that you have both key and foreign, type and size match,
for eg. person.id int (10) must mapped to service_customer.person_id in (10)
if the type and size differ, mysql will complain.