类似字符的唯一约束违规

Posted

技术标签:

【中文标题】类似字符的唯一约束违规【英文标题】:Unique constraint violation on similar characters 【发布时间】:2018-07-11 22:12:48 【问题描述】:

我对 MariaDB 中的主键有疑问(我希望 mysql 也一样)。我正在构建一个 Django 应用程序,其中一些重要的表由 Django 使用以下 SQL 创建:

CREATE TABLE table_name
(
  term     VARCHAR(64) NOT NULL PRIMARY KEY,
  enabled  TINYINT(1)  NOT NULL,
  added    DATE        NOT NULL,
  verified DATE        NOT NULL
) ENGINE = InnoDB;

如果我在此表中插入两行,其中term 第一行是ó,另一行是o,我会收到错误[23000][1062] Duplicate entry 'o' for key 'PRIMARY'。其他类似的字母也会发生相同的行为,例如 eé。 我真的觉得这种行为很奇怪,因为 SQLite 没有问题。

所以现在的问题是:如何让数据库引擎将它们视为不同的值?

额外问题:我可以期待 PostgreSQL 的相同行为吗?

已经在 SQL 中尝试过这个,而不仅仅是 Django。

【问题讨论】:

【参考方案1】:

好吧,碰巧,我在写这个问题时自己回答了这个问题。我意识到我没有尝试过字符集utf8mb4 和排序规则utf8mb4_bin 的组合,我记得_bin 排序规则在二进制级别比较字符,没有任何规则使相似字符看起来相似。所以用

创建我的数据库
CREATE DATABASE [database_name] CHARACTER SET = 'utf8mb4' COLLATE = 'utf8mb4_bin';

成功了。或者我可以用

创建每个表
CREATE TABLE [table_name] CHARACTER SET = 'utf8mb4' COLLATE = 'utf8mb4_bin';

,但是在数据库中设置它是这里唯一可行的选择,因为 Django 负责创建表。

更新:

关于奖励问题,简短的回答是:不。

详情见下方nick-barnes评论。

更新 2:

Microsoft SQL Server(在 SQL Server 2017 CU3 上测试)使用排序规则来比较字符串是否相等,因此需要使用二进制排序规则(例如 Latin1_General_BIN)来解决此服务器上的这个问题。

【讨论】:

Postgres 在测试字符串是否相等时总是使用二进制比较,因此不会混淆o 是否等于ó(在主键列中或其他任何地方) .排序顺序一直存在不一致,因为排序规则被委托给主机操作系统,但在 Postgres 10 中处理了这一问题,并包含了ICU support。 答案已更新,我已通过测试验证了该行为。感谢您的洞察力。 MySQL:更改DATABASETABLE 的设置仅分别更改新添加的表或列的默认值。你需要ALTER TABLE ... MODIFY COLUMN ...

以上是关于类似字符的唯一约束违规的主要内容,如果未能解决你的问题,请参考以下文章

如何绕过唯一约束违规?

具有唯一索引和主键的列给出唯一约束违规

在 Hibernate 中使用 SEQUENCE 时导致唯一约束违规的原因是啥?

SELECT查询在Oracle中给出唯一约束违规

具有序列提供的 id 的主键列上的唯一约束违规

HSQLDB:奇怪的“唯一约束或索引违规”,从 CSV 读取数据