SQLite: увеличить уникальное целочисленное поле - PullRequest
3 голосов
/ 09 октября 2011

У меня есть таблица, которая содержит уникальное целочисленное поле, содержащее последовательные значения. Когда я пытаюсь увеличить эти значения, используя метод ниже, я нарушаю ограничение уникальности. Есть ли способ сделать это успешно?

CREATE TABLE numbers(num INT UNIQUE NOT NULL)
UPDATE numbers SET num=num+1

Ответы [ 3 ]

10 голосов
/ 09 октября 2011

Это должно быть ошибкой.Это, очевидно, не будет вызывать нарушение ограничений, и это работает в 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 numbers SET num = -num;
UPDATE numbers SET num = 1 - num;
3 голосов
/ 09 октября 2011

Этого можно достичь:
- найти максимальное значение max_pk
- обновить все строки с помощью pk = pk + max_pk.
- обновить все строки с помощью pk = pk - max_pk + 1

0 голосов
/ 08 мая 2012

Поведение правильное.Рассмотрим случай, когда 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...