我有一个表,其中包含一个包含连续值的唯一整数字段。当我尝试使用下面的方法增加这些值时,我违反了唯一约束。有成功的方法吗?
CREATE TABLE numbers(num INT UNIQUE NOT NULL)
UPDATE numbers SET num=num+1
答案 0 :(得分:10)
这必须是一个错误。它显然不会导致约束违规,并且它在SQL Server中有效。事实上,我确信这是一个错误,因为如果我按降序插入数字,我可以成功:
sqlite> INSERT INTO numbers (num) VALUES (3);
sqlite> INSERT INTO numbers (num) VALUES (2);
sqlite> INSERT INTO numbers (num) VALUES (1);
sqlite> UPDATE numbers SET num = num + 1;
sqlite> SELECT * FROM numbers;
4
3
2
UPDATE的正确性不应该取决于表中行的顺序。
作为一种简单的解决方法,您可以这样做:
UPDATE numbers SET num = -num;
UPDATE numbers SET num = 1 - num;
答案 1 :(得分:3)
你应该能够通过以下方式实现这一点:
- 找到最大值max_pk
- 使用pk = pk + max_pk更新所有行
- 使用pk = pk更新所有行 - max_pk + 1
答案 2 :(得分:-1)
行为是正确的。考虑num
是外键约束中的引用列并且更新是级联的情况。除了通过游标更新(包括允许延迟完整性检查直到提交时间)而实现更新的任何方法违反临时更新中的唯一约束将导致数据库不一致(完整性丧失)。例如,如果使用num = n+1
更新了行num = n
后,n
的行已更新。有一些方法可以“重新短语”更新以避免这种情况,但它需要领域知识,因此引擎无法为您完成。也不应该。
Create table numbers (num int unique);
Create table others (a int, num int unique references numbers (num) on update cascade);
Insert into numbers values (1), (2), (3), (4);
Insert into others values (1,1), (2,2), (3,3), (4,4);
然后,任何违反唯一约束的num
临时更新都会导致完整性丢失,无论行顺序如何,更新都会失败。除非在SQL语句中明确说明,否则您不应该依赖行顺序。关闭约束检查(或将其推迟到提交时间)将“不知道你在做什么”的后果直接放在程序员手中(它所属的位置)。如果你对数据库有足够的了解来禁用完整性检查,你应该对后果(如果有的话)感到震惊。
在没有这些副作用的情况下进行更新的唯一方法是确保更新没有任何临时(逐行)违反完整性的行为。
在其他一些引擎中你会使用:
update numbers set num = num + 1 from numbers order by num desc;
通过“游标当前”执行更新来控制行处理顺序,每次都会获得正确的结果。
也许一个有用的增强功能是允许更新使用from和order by子句,从而允许直接表达这样的更新。实际上,这样的更新将成为“select
”,而不是返回行,“返回行”将被更新操作替换...
有些人将此称为可更新视图。它真的不是。它仍然是“游标当前”的更新,通过允许将其他表连接到游标中,只对结果集中的每个有效结果行进行更新,而不是将游标限制为仅单个更新的表。 / p>
您可以通过创建具有正确排序的视图,然后在视图上更新基础表,然后针对视图执行更新的num
列的更新触发器来实现此目的:
Create table numbers (num int unique);
Create table others (a int, num int unique references numbers (num) on update cascade);
Insert into numbers values (1), (2), (3), (4);
Insert into others values (1,1), (2,2), (3,3), (4,4);
Create view updatenumbers
as
select num from numbers order by num desc;
Create trigger updnum instead of update of num on updatenumbers
begin
update numbers set num = new.num where num=old.num;
end;
update updatenumbers set num = num + 1;
sqlite> select * from numbers; select * from others;
2
3
4
5
1|2
2|3
3|4
4|5
要完全正确,您应该使用rowid
在基础表上执行更新。创建视图以实现游标以及替代触发器以基于rowid更新基础表然后成为实现“当前游标”样式更新的通用模式。然后可以使触发器足够通用(如果需要),它可以根据游标行的任何选择通过游标更新任何列或列组合......并且仍然可以维护所有引用约束。
create table numbers (num int unique);
create table others (a int, num int unique references numbers (num) on update cascade);
insert into numbers values (1), (2), (3), (4);
insert into others values (1,1), (2,2), (3,3), (4,4);
create view updatenumbers
as
select numbers.rowid, *
from numbers
order by num desc;
create trigger updnum instead of update of num on updatenumbers
begin
update numbers
set num = new.num
where rowid=old.rowid;
end;
update updatenumbers set num = num + 1;
select * from numbers;
select * from others;
2
3
4
5
1|2
2|3
3|4
4|5