Поведение правильное.Рассмотрим случай, когда 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;
, чтобы управлять порядком обработки строк, выполняя обновление через «текущий»of курсора ", который всегда будет получать правильный результат каждый раз.
Возможно, полезным усовершенствованием было бы разрешение использования обновления и упорядочения по предложениям, таким образом позволяя такому обновлению быть выраженным непосредственно.По сути, такое обновление могло бы стать «select
», где вместо возврата строк «возврат строки» будет заменен операцией обновления ...
Некоторые называют это обновляемым представлением.Это действительно не так.Это все еще обновление «current of курсора», обновление происходит только для каждой действительной строки результатов в наборе результатов, позволяя присоединять дополнительные таблицы к курсору, а не ограничивать курсор только одной обновленной таблицей.
Вы можете достичь этого в настоящее время, создав представление с правильным порядком, а затем триггер обновления для столбца 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