Is there any point of using COLLATE per column when creating tables? - mysql

I just did an export of a MySQL database in order to duplicate it on another server. Looking at the sql script I see the following:
CREATE TABLE `X` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`email` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL
PRIMARY KEY (`id`),
KEY `name_index` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=8366
DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Do I need to use this per column when collate is set for the table?
I think the reason for why each column has this, was because I wanted to make sure I could store locale values such as æ, ø å.

Since your column-level collations are all the same, setting the collation at the table level will have the same effect.
Note that collation has no effect on what you can store; rather, it affects how the results are sorted. The only reason you would want a different collation per column would be if your table contains columns with data for different locales requiring a different sort order.

Related

Replace the statement ON DUPLICATE KEY UPDATE

MySQL version 5.7, ONLY_FULL_GROUP_BY is enabled
Please tell me an effective way to solve the following problem: it is necessary to make an entry in the table only if there are no matches in the record for several fields. If there is a matching record, then we update it with new data.
The ON DUPLICATE KEY UPDATE statement does not suit me, because the type and uuid_session fields are not unique (for example, there may be several records with different type, but with the same uuid_session).
Here is an example of a fake-query to better understand my question:
INSERT INTO cameraStatus(time, uuid_session)
VALUES ('2022-12-14 16:01:00', '01234567-8901-2345-6789-012345678901')
ON DUPLICATE KEY UPDATE (type, uuid_session) = ("WORK", "55555555-8901-2345-6789-012345678901");
My table:
CREATE TABLE `cameraStatus` (
`id` int NOT NULL,
`camera_id` int NOT NULL DEFAULT '0',
`time` timestamp NOT NULL DEFAULT '2021-12-31 21:00:00',
`type` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'INFO',
`message` mediumtext COLLATE utf8_unicode_ci,
`uuid_session` varchar(36) CHARACTER SET utf8mb3 COLLATE utf8_unicode_ci DEFAULT '01234567-8901-2345-6789-012345678901'
)

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.

Altering collation on field causes UNIQUE constraint to fail

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.

MySQL alter table gives unknown column error

I'm renaming a bunch of columns in a lot of my tables and changing their data types for a major system update, and I haven't had many issues except for this one.
Error Code: 1054. Unknown column 'FactoryID' in 'factories'
show create table `factories`;
CREATE TABLE `factories` (
`FactoryID` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
`ParentFactoryID` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`Name` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`Notes` varchar(1024) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`DateTimeAdded` datetime DEFAULT NULL,
`CountryID` smallint(5) unsigned NOT NULL,
`ListID` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`Deleted` int(1) DEFAULT '0',
PRIMARY KEY (`FactoryID`),
UNIQUE KEY `FactoryID` (`FactoryID`),
KEY `ParentFactoryID` (`ParentFactoryID`),
KEY `CountryID` (`CountryID`),
CONSTRAINT `factories[CountryID]` FOREIGN KEY (`CountryID`) REFERENCES `countries` (`CountryID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `sterling`.`factories`
CHANGE COLUMN `CountryID` `CountryID` SMALLINT(5) UNSIGNED NOT NULL AFTER `FactoryID`,
CHANGE COLUMN `ParentFactoryID` `_OriginalFactoryID` CHAR(36) CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci' NULL DEFAULT NULL AFTER `ListID`,
CHANGE COLUMN `DateTimeAdded` `__Added` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) AFTER `__Active`,
CHANGE COLUMN `Deleted` `__Active` TINYINT(1) NOT NULL DEFAULT 1 ,
ADD COLUMN `__Updated` TIMESTAMP(6) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(6) AFTER `__Added`,
drop primary key,
change column`FactoryID` `#FactoryID`char(36)null after`__Updated`,
add column`FactoryID`binary(8)not null first,
add index`#FactoryID`(`#FactoryID`);
Am I missing an order of operations sort of thing? If all of those changes happen in order, I'm not sure what exactly the problem is, since at no time that FactoryID is referenced does it not exist.
The columnname that after refers to is supposed to be the new name of the column in the final table. It doesn't matter where in the statement you change the name.
And it actually even makes this following statement fail:
alter table tablename
add column b int after a,
drop column a
Error Code: 1054. Unknown column 'a' in 'tablename'
as in the final table, there will be no column a anymore, so it is invalid even if you drop a only after you already added column b.
In your case, you would need to change
CHANGE COLUMN `CountryID` `CountryID` ... AFTER `FactoryID`,
to
CHANGE COLUMN `CountryID` `CountryID` ... AFTER `#FactoryID`,
in order to anticipate that you will (later) rename the column FactoryID to #FactoryID.
To make it complete: in after, you cannot refer to a column that you will add later. For example, in the end of your statement, you actually add another column FactoryID, but you cannot yet refer to it here (otherwise, the query would not have failed). You could add that column first though (and even before you rename the original FactoryId, MySQL allows you to swap columnnames that way). CHANGE COLUMN CountryID CountryID ... AFTER FactoryID would then work, but would refer to the new column (so in total, CountryID would be the 2nd column, which may or may not be what you intended).
I don't know if it is officially documented somewhere, you will probably have to take it as convention, but it has "always" been that way.

MySQL sorting Ä and Ö with utf8_general_ci

Is there any way to sort characters Ä and Ö correctly without changing collation of connection? I'm using PHP and PDO.
If it's not sensible... I tried to convert every table to utf8_swedish_ci using
ALTER TABLE tablename COLLATE utf8_swedish_ci; but it seems not working. There are still Ä before A. What's wrong?
EDIT: I manually set every field to utf8_swedish_ci and now it's working, but is there any easier way to that?
Take this table as example:
CREATE TABLE `countries` (
`countryid` INT(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`country` VARCHAR(44) NOT NULL,
PRIMARY KEY (`countryid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
For the country column to sort correctly use this query:
ALTER TABLE `countries`
CHANGE COLUMN `country` `country` VARCHAR(44) NOT NULL COLLATE 'utf8_swedish_ci' AFTER `countryid`;
As you mentioned you need the collate on the column you want to sort.