mysql – 如何在没有键限制的情况下防止重复的VARCHAR?
作者:互联网
我想将URL存储在数据库列中,并强制执行值必须唯一的约束.不幸的是,MySQL对索引键的长度有限制,这意味着只检查URL的前X个字符的唯一性.因此,我遇到了误报,其中两个不同的URL触发了约束集成违规,因为前X个字符恰好相同.
有没有办法在VARCHAR列上强制执行唯一性而不限制其长度?
例如,是否可以在前X个字符上创建非UNIQUE索引,然后如果其余字符相同则具有触发器块INSERT?
解决方法:
我们一直给你答案,不直接回答这个问题,因为这就是我们解决这个问题的方法.无限长度的索引是不切实际且效率低的,但是由于天文学上有意义的碰撞可能性低,因此唯一的散列提供了足以完成任务的解决方案.
与其他提供的解决方案类似,我的标准方法不预先检查重复 – 在这个意义上它是乐观的:它依赖于数据库的约束检查,假设大多数插入不是重复,所以没有意义浪费时间试图确定它们是否存在.
经过测试的工作示例(5.7.16,向后兼容5.6;以前的版本没有内置的TO_BASE64()函数):
CREATE TABLE web_page (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
url LONGTEXT NOT NULL,
url_hash CHAR(24) COLLATE ascii_bin,
PRIMARY KEY(id),
UNIQUE KEY(url_hash),
KEY(url(16))
)ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED;
请注意,我正在存储hash64的base64版本.与以二进制形式存储它相比,这是4:3大小的权衡,因为它使表内容和错误消息成为可读的,并且低效率被表压缩部分抵消.哈希列具有唯一约束.数据类型是CHAR,而不是VARCHAR,因为这消除了存储大小所需的字节 – 散列总是固定大小.该列使用带有ascii_bin(区分大小写)排序规则的ascii字符集,使列和唯一索引保持尽可能小.
url_hash由下面的触发器设置,但触发器不检查冲突 – 由于url_hash上的唯一约束,因此无需检查.数据库将阻止重复插入.
请注意,url_hash应该已被声明为NOT NULL但MySQL在BEFORE INSERT触发器触发之前错误地强制执行此操作,而不是之后,因此我们受此限制.触发器确实阻止它为空.
url列的前缀索引长度为16,可任意选择.这不是一个唯一的约束,只是一个查找索引,它可能比你想要的更短,但它的长度对我们正在解决的问题没有操作上的影响.
这是设置url_hash的触发器.插入行时,我们不需要在INSERT语句中包含此值.
DELIMITER $$
DROP TRIGGER IF EXISTS web_page_bi $$
CREATE TRIGGER web_page_bi BEFORE INSERT ON web_page FOR EACH ROW
BEGIN
SET NEW.url_hash = TO_BASE64(UNHEX(MD5(NEW.url)));
END $$
DELIMITER ;
您还需要更新触发器,如果表应该是不可变的,则阻止更新,或者如果URL更改则更新哈希.我们还需要这个触发器来确保url_hash列不能被不适当地设置为NULL,因为MySQL中的限制不允许我们按照我们应该的方式实际声明它.
现在,来测试一下.
mysql> INSERT INTO web_page (url) VALUES ('http://example.com/');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM web_page;
+----+---------------------+--------------------------+
| id | url | url_hash |
+----+---------------------+--------------------------+
| 1 | http://example.com/ | pr8XV//wV/JmtpffnPF2/Q== |
+----+---------------------+--------------------------+
1 row in set (0.00 sec)
到现在为止还挺好.现在,一个不同的URL:
mysql> INSERT INTO web_page (url) VALUES ('http://example.net/');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM web_page;
+----+---------------------+--------------------------+
| id | url | url_hash |
+----+---------------------+--------------------------+
| 1 | http://example.com/ | pr8XV//wV/JmtpffnPF2/Q== |
| 2 | http://example.net/ | ZVk/eLfvBI6tHN0Luj3NnQ== |
+----+---------------------+--------------------------+
2 rows in set (0.00 sec)
仍然有效.现在,重复一次.
mysql> INSERT INTO web_page (url) VALUES ('http://example.com/');
ERROR 1062 (23000): Duplicate entry 'pr8XV//wV/JmtpffnPF2/Q==' for key 'url_hash'
完善.如果您希望哈希冲突的风险比MD5提供的更低,请使用SHA变体,将data_hash的长度增加到CHAR_LENGTH(TO_BASE64(UNHEX(/ *您的哈希函数* /)))以适应哈希算法生成的值正在使用.
标签:mysql,varchar,unique-key 来源: https://codeday.me/bug/20190807/1605148.html