Django/MySQL:即使字段值是唯一的,使非唯一字段唯一也会失败

Posted

技术标签:

【中文标题】Django/MySQL:即使字段值是唯一的,使非唯一字段唯一也会失败【英文标题】:Django/MySQL: Making non-unique field unique fails even if field values are unique 【发布时间】:2018-04-21 21:26:09 【问题描述】:

我目前有这个:

class Committee(models.Model):
    # ...some fields...
    committee_xml_id = models.IntegerField()

我需要使字段 committee_xml_id 独一无二,即:

class Committee(models.Model):
    # ...some fields...
    committee_xml_id = models.IntegerField(unique=True)

我也试过这样做:

class Committee(models.Model):
    # ...some fields...
    committee_xml_id = models.IntegerField(unique=True, db_index=False)

唉,结果是一样的。

运行./manage.py makemigrations 和随后运行./manage.py migrate,问题是这样的:

django.db.utils.OperationalError: (1061, "Duplicate key name 'appname_committee_committee_xml_id_d1210032_uniq'")

乍一看,问题似乎是表中已经存在非唯一数据,但问题恰恰是不存在。表中只有 45 行,字段 committee_xml_id 仅包含唯一条目。

当没有重复时,以下查询没有结果:

SELECT
    com.committee_xml_id,
    COUNT(*)
FROM
    appname_committee AS com
GROUP BY
    com.committee_xml_id
HAVING
    COUNT(*) != 1

为了严谨,这里是没有HAVING-条件的相同查询,清楚地表明确实没有重复:

SELECT
    com.committee_xml_id,
    COUNT(*)
FROM
    appname_committee AS com
GROUP BY
    com.committee_xml_id

结果是:

# committee_xml_id, COUNT(*)
78, 1
79, 1
124, 1
125, 1
129, 1
130, 1
131, 1
132, 1
133, 1
134, 1
137, 1
139, 1
140, 1
141, 1
142, 1
147, 1
148, 1
149, 1
150, 1
151, 1
152, 1
153, 1
154, 1
160, 1
166, 1
167, 1
168, 1
169, 1
170, 1
176, 1
192, 1
193, 1
194, 1
195, 1
198, 1
199, 1
200, 1
201, 1
202, 1
203, 1
204, 1
205, 1
206, 1
207, 1
216, 1

非常感谢任何帮助。

【问题讨论】:

数据库可能有一个复杂的历史(也是手动管理的,不仅是通过迁移,或者--fake,可能是在初始迁移之后修改了一些迁移文件。)迁移中可能会出现混乱索引存在,但相关迁移未记录在“django_migrations”表中。尝试备份数据库和DROP INDEX appname_committee_committee_xml_id_d1210032_uniq ON appname_committee;,然后应用迁移。或者你可以先python manage.py sqlmigrate app_label migration_name查看迁移SQL的内容,看看是不是唯一可能的冲突... ... 例如还没有创建一个尚存在的字段等。如果这种方式有帮助,我会写一个答案。这是一个广泛的问题:A)它是如何可能的? (需要更多信息) B)如果所有数据都是有价值的,如何修复它。 (my general answer) C) 如果生产中还没有数据库并且开发中没有有价值的数据,如何在开发中最容易地修复它。 (许多琐碎的答案)||你能在一个新的数据库上重现这个问题吗? (可能不是) 这被证明是数据库上的某种半完成工作,还没有真正完成,这把一切都搞砸了。感谢您的提示,@hynekcer! 好的。删除索引是安全和简单的。如果仍然存在更多冲突,请查看我的一般答案,我现在在其中添加了一个链接,如何通过sqlmigratemigrate --fake 的选择性复制粘贴来修复有些损坏的数据库。 【参考方案1】:

错误与表中的 数据 无关。如果我们尝试插入违反唯一约束的数据,或者如果我们尝试在表中有重复项时定义唯一键,我们会看到不同的错误:

错误代码 1062:重复条目 ...


1061 错误发生在我们尝试定义一个新键并且该键已经存在时。

作为演示:

  create table foo2 (id int);

  0 row(s) affected

  insert into foo2 (id) values (1),(1);

  2 row(s) affected

  alter table foo2 add unique key foo2_ux1 (id);

错误代码:1062 键“foo2_ux1”的重复条目“1”

  alter table foo2 add key foo2_ix2 (id);

  0 row(s) affected

  alter table foo2 add key foo2_ix2 (id);

错误代码:1061 Duplicate key name 'foo2_ix2'

  alter table foo2 add UNIQUE key foo2_ix2 (id);

错误代码:1061 Duplicate key name 'foo2_ix2'

SHOW CREATE TABLE 将向我们显示该名称的键实际上已经存在

  CREATE TABLE `foo2` (
  `id` INT(11) DEFAULT NULL,
  KEY `foo2_ix2` (`id`)
  ) ENGINE=InnoDB

【讨论】:

感谢您的详细说明,但是当它第一次被制作时,是什么让该密钥已经存在? 我将您的答案标记为正确答案,因为这确实是之前尝试运行迁移失败的情况已经存在的该死的事情。直到我尝试使用较旧的数据库副本,这才变得明显,此时问题自行解决。谢谢!

以上是关于Django/MySQL:即使字段值是唯一的,使非唯一字段唯一也会失败的主要内容,如果未能解决你的问题,请参考以下文章

SQL:根据最近的日期选择一个字段中的值是唯一的记录

如何确保在 SQL 数据库字段中输入的值是唯一的?

唯一约束

规约先行-(十五)索引规约

MySQL开发索引创建规范

表完整性约束