lrk*_*kwz 7 mysql mariadb foreign-key ddl
我有两个现有的 mysql/mariadb 表,例如:
CREATE TABLE `ice_product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`long_description` text DEFAULT NULL,
`long_summary_descr` text DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`quality` varchar(255) DEFAULT NULL,
`release_date` date DEFAULT NULL,
`update_date` date DEFAULT NULL,
`short_description` text DEFAULT NULL,
`short_summary_descr` text DEFAULT NULL,
`title` varchar(255) DEFAULT NULL,
`category_id` bigint(20) DEFAULT NULL,
`supplier_id` bigint(20) DEFAULT NULL,
`updated` bit(1) NOT NULL,
`product_family_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_ice_product_cat` (`category_id`),
KEY `idx_ice_product_supp` (`supplier_id`),
KEY `idx_ice_product_updated` (`updated`),
CONSTRAINT `fk_icecat_product_cat_id` FOREIGN KEY (`category_id`) REFERENCES `ice_category` (`id`),
CONSTRAINT `fk_icecat_product_supp_id` FOREIGN KEY (`supplier_id`) REFERENCES `ice_supplier` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
Run Code Online (Sandbox Code Playgroud)
和
CREATE TABLE `ice_product_manuals` (
`product_id` bigint(20) NOT NULL,
`descr` varchar(255) DEFAULT NULL,
`url` varchar(800) DEFAULT NULL,
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_manual` (`product_id`,`url`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
Run Code Online (Sandbox Code Playgroud)
我想定义一个外键约束
ALTER TABLE ice_product_manuals ADD CONSTRAINT fk_icecat_manuals_product_id FOREIGN
KEY (product_id) REFERENCES ice_product(`id`);
Run Code Online (Sandbox Code Playgroud)
但该声明失败了
ERROR 1005 (HY000): Can't create table `mydb`.`ice_product_manuals` (errno: 150 "Foreign key constraint is incorrectly formed")
Run Code Online (Sandbox Code Playgroud)
并SHOW ENGINE INNODB STATUS显示以下内容
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2023-04-15 19:05:36 0x7f70e4065640 Error in foreign key constraint of table `mydb`.`ice_product_manuals`:
Alter table `mydb`.`ice_product_manuals` with foreign key `fk_icecat_manuals_product_id` constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Run Code Online (Sandbox Code Playgroud)
哪里有问题?
您需要在 上的引用表上有一个索引ice_product_manuals (product_id)。
请参阅:dbfiddle-1
由于特殊原因,您拥有的复合索引不能用于 FK 约束。通常,如果约束位于索引的第一列,则可以将两个(或更多)列索引用于 FK 约束。
然而, MariaDB有一个功能MDEV-371UNIQUE ,它为非常长的列(超过 3072 字节)实现了索引,其中指出:
允许用户创建任意长度的唯一约束。这将在上层服务器中完成,而不是在引擎中完成。服务器将创建不可见的虚拟列,该虚拟列具有对唯一列的哈希值。
BTREE以及该列的正常索引。在插入或更新时,它将检查索引是否存在哈希冲突,并且如果需要,将检索实际行以比较数据。”
您的复合索引确实位于非常长的(第二)列(utf8mb4 上有 800 个字符),超过 3072 个字节,因此它属于这种情况。
所以这个BTREE索引是在两列的哈希上,它不能用于FK检查。
请参阅:dbfiddle-2,如果第二列较短,则代码可以工作,并且不需要创建此 btree-on-a-hash,而是创建普通的 2 列 btree 索引。