我正在尝试对我正在更新的列具有唯一约束的表进行批量更新。假设该表定义为:
CREATE TABLE foo (id INTEGER PRIMARY KEY, bar INTEGER UNIQUE);
假设数据库包含一系列行,其
bar
列中的连续整数值范围从 1 到 100,并且它们已按顺序插入。
假设我想在从 17 开始的“bar”序列中放置一个 5 宽的间隙,例如使用如下查询:
UPDATE foo SET bar = bar + 5 WHERE bar > 17;
SQLite 拒绝执行此更新,说“
Error: UNIQUE constraint failed: foo.bar
”好吧,当然,如果查询一次执行一行并从满足 WHERE 子句的第一行开始,确实会违反 UNIQUE 约束:两个rows 将有一个值为 23 的 bar
列(bar
为 18 的行,以及 bar
为 23 的原始行)。但是,如果我能以某种方式强制 SQLite 自下而上运行更新(从 row
的最高值开始并向后工作),则不会违反 UNIQUE 约束。
SQLite 有一个可选的用于 UPDATE 的 ORDER BY / LIMIT 子句,但这不会影响 UPDATE 发生的顺序;如本页底部所述,“修改行的顺序是任意的。”
是否有一些简单的方法建议 SQLite 按特定顺序处理行更新?或者我是否必须使用更复杂的路线,例如子查询?
更新:这不起作用;出现同样的错误:
UPDATE foo SET bar = bar + 5 WHERE bar IN
(SELECT bar FROM foo WHERE bar > 17 ORDER BY bar DESC);
如果将唯一约束从表定义中移出到其自己的独立索引中是可行的,那么实现 Ben 的想法就会变得很容易:
CREATE TABLE foo(id INTEGER PRIMARY KEY, bar INTEGER);
CREATE UNIQUE INDEX bar_idx ON foo(bar);
-- Do stuff
DROP INDEX bar_idx;
-- Update bar values
CREATE UNIQUE INDEX bar_idx ON foo(bar); -- Restore the unique index
如果没有,类似
CREATE TEMP TABLE foo_copy AS SELECT * FROM foo;
-- Update foo_copy's bar values
DELETE FROM foo;
INSERT INTO foo SELECT * FROM foo_copy;
不需要更改表的另一种方法是进行中间更新,将新值设置在现有范围未涵盖的范围内(如果没有值可以为负数,则很容易),然后更新价值观应有的样子。
例如下面使用负中间值演示了这一点:-
-- Load the data
DROP TABLE IF EXISTS foo;
CREATE TABLE foo (id INTEGER PRIMARY KEY, bar INTEGER UNIQUE);
WITH RECURSIVE cte1(x) AS (SELECT 1 UNION ALL SELECT x + 1 FROM cte1 LIMIT 100)
INSERT INTO foo (bar) SELECT * FROM cte1;
-- Show the original data
SELECT * FROM foo;
UPDATE foo SET bar = 0 - (bar + 5) WHERE bar > 17;
UPDATE foo SET bar = 0 - bar WHERE bar < 0;
-- Show the end result
SELECT * FROM foo;
我也有类似的情况,但就我而言,这是一个排序顺序。 不同之处在于它是一个正整数。 所以我可以分两步更新它。 在你的例子中,它会是这样的:
UPDATE foo SET bar = -(bar + 5) WHERE bar > 17;
UPDATE foo SET bar = - bar WHERE bar < 0;
或者如果该值存在自然限制或限制:
UPDATE foo SET bar = (bar + 5) + 1000000 WHERE bar > 17;
UPDATE foo SET bar = bar - 1000000 WHERE bar > 1000000;
我认为这比删除/重新创建约束或重新创建表更好。