【问题标题】:Mysql temporarily suppress unique indexMysql暂时抑制唯一索引
【发布时间】:2012-01-14 16:48:32
【问题描述】:

我有一个在两列上具有唯一索引的表,确切地说是 id_parent 和 sort_order

+----+-----------+------------+-------------+-------------+-------------+
| id | id_parent | sort_order | some_data   | other_data  | more_data   |
+----+-----------+------------+-------------+-------------+-------------+
| 1  |         1 |          1 | lorem ipsum | lorem ipsum | lorem ipsum |
| 2  |         1 |          2 | lorem ipsum | lorem ipsum | lorem ipsum |
| 3  |         1 |          3 | lorem ipsum | lorem ipsum | lorem ipsum |
+----+-----------+------------+-------------+-------------+-------------+

现在我想一次性更新它们、它们的数据和 sort_order。 sort_order 将从1 - 2 - 3 更改为,例如2 - 3 - 1

但是当我开始运行更新语句时,唯一索引阻止了我,正如预期的那样,说我不能有两行 id_parent = 1 and sort_order = 2。 好吧,我现在可以将其设置为 4,以正确的顺序更新其他行,然后设置这一行。 但是,我将不得不运行一个额外的语句,并且很可能会在我的脚本语言中添加额外的逻辑来确定正确的更新顺序。 我也用ORM,就更不方便了。

我现在的问题是,有什么方法可以让mysql暂时忽略这个索引吗?就像开始一个特殊的事务,其中的索引只会在提交之前计算?

【问题讨论】:

标签: mysql sql indexing sql-update


【解决方案1】:

据我所知,这是不可能的。

我唯一见过这样的事情是您可以禁用 myisam 表上的非唯一键。但不在 InnoDB 上,也不在唯一键上。

但是,为了节省一两次更新,不需要确切的数字 1、2 和 3。您也可以拥有 4、5 和 6。对吗?您将按顺序使用它,而不是其他任何东西,因此确切的数字并不重要。如果您很聪明,它甚至可以为您保存更新。从你的例子中

update table set sort_order = 4 where sort_order = 1 and id = 1 and id_parent = 1;

新的排序顺序是 2、3、1。只需一次更新。

【讨论】:

  • 真丢脸,使用 4-5-6 太明显了。
  • 同意多次更新是不可能的。但是,在单个语句中更新它们可能会起作用。无论哪种方式,对于一个清晰简单的解决方案都是 +1。
【解决方案2】:

‘但是当我开始运行 update statements...’ – 我理解,您尝试使用多个 UPDATE 语句更新值,就像在一个循环中一样。是这样吗?一口气更新它们怎么样?比如这样:

UPDATE atable
SET sort_order = CASE sort_order WHEN 3 THEN 1 ELSE sort_order + 1 END
WHERE id_parent = 1
  AND sort_order BETWEEN 1 AND 3

单个语句是原子的,因此,到此更新结束时,sort_order 的值虽然发生了变化,但仍然是唯一的。

抱歉,我无法在 MySQL 中对此进行测试,但它在 SQL Server 中绝对有效,而且我相信这种行为符合标准。

【讨论】:

  • 没想到。不过,与 ORM 一起使用会很痛苦。这更接近于指定问题的正确答案,然后是 Andreas 的回答,但我可能最终会使用他的建议。在这种情况下,我应该如何处理选定的答案?把它交给我将要使用的答案,或者回答哪个更接近主题?
  • 好吧,如果有多个答案与问题相匹配,你可能应该选择你将要应用的那个来解决实际问题。因此,您应该选择 Andreas 的答案。不过,可能存在其他考虑因素。无论如何,我同意你的决定!
  • 除非行实际上以正确的顺序更新,否则这将不起作用。在执行期间任何时候都不能违反唯一密钥。 update sorttest set sort = sort+1 order by sort desc; 在没有降序的情况下将无法工作,第一行将违反密钥,即使第二行即将被移出原位。
  • @AndreasWederbrand:感谢您的反馈。关于 MySQL,我无话可说,但我的查询在 SQL Server 中确实有效。您的示例也有效,只是没有 ORDER BY,因为 SQL Server 不允许在 UPDATE 语句中使用 ORDER BY。您说得对,列上的唯一键在执行过程中被违反,但是(在 SQL Server 中)约束在语句完成更新后才会生效。并且由于最终数据不包含任何重复项,因此不会发生异常,因此也不会回滚。
  • 一次性更新在 Oracle 中也很有效。我对 mysql 感到沮丧。
【解决方案3】:

MyISAM

对于 MyISAM 表,您只需在脚本开头添加以下行:

SET UNIQUE_CHECKS=0;

通常将此与以下内容结合使用:

SET FOREIGN_KEY_CHECKS=0;

这里的文档中提到了UNIQUE_CHECKS 变量:
http://dev.mysql.com/doc/refman/5.0/en/converting-tables-to-innodb.html

InnoDB

似乎有报告称上述命令不适用于 InnoDB 引擎。如果是这样,那么您可以尝试暂时删除 UNIQUE 索引,然后将其添加回来。

见:How to remove unique key from mysql table

如果您有代码示例,请随时编辑这篇文章并通过代码示例对其进行改进。

【讨论】:

  • 刚刚在 5.6 上尝试过,不适用于 InnoDB。重复键仍然引发错误。
  • @bluecollarcoder 您是否尝试将 both UNIQUE_CHECKSFOREIGN_KEY_CHECKS 设置为零?您确定您没有复制可能仍然不允许的 主键
  • 唯一键位于不引用任何其他表的列上,因此FOREIGN_KEY_CHECKS 在这里无关紧要。并且具有唯一键的列绝对不是主键的一部分。在这种情况下,您在什么情况下能够让 UNIQUE_CHECKS=0 按预期工作?
  • 引用自 Mysql 文档:unique_checks permits but does not require storage engines to ignore duplicate keys. Innodb 似乎不支持此功能。
  • SET UNIQUE_CHECKS=0;-不会帮助您暂时禁止唯一键。如果设置为 0,则允许存储引擎假定输入数据中不存在重复键。如果您确定您的数据不包含唯一性违规,则可以将其设置为 0。请注意,将此变量设置为 0 并不要求存储引擎忽略重复键,如果检测到
猜你喜欢
  • 2010-09-28
  • 2015-03-26
  • 1970-01-01
  • 1970-01-01
  • 2011-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多