Вставка Sqlite с уникальными именами, получение идентификатора

У меня есть список строк для вставки в БД. Они ДОЛЖНЫ быть уникальными. Когда я вставляю, мне нужен их идентификатор (для использования в качестве внешнего ключа в другой таблице), поэтому я использую last_insert_rowid. Я получаю 2 проблемы.

  1. Если я использую замену, их идентификатор (INTEGER PRIMARY KEY) обновляется, что нарушает мою базу данных (записи указывают на несуществующие идентификаторы)
  2. Если я использую игнорировать, rowid не обновляется, поэтому я не получаю правильный идентификатор

Как мне получить их идентификаторы? если мне это не нужно, я бы не хотел использовать оператор select для проверки и вставки строки, если она не существует. Как мне это сделать?


person Community    schedule 10.05.2009    source источник


Ответы (4)


Когда происходит нарушение ограничения UNIQUE, алгоритм REPLACE удаляет ранее существовавшие строки, вызывающие нарушение ограничения, перед вставкой или обновлением текущей строки, и команда продолжает выполняться в обычном режиме. Это приводит к изменению rowid и создает следующую проблему

Y:> **sqlite3 test**  
SQLite version 3.7.4  
Enter ".help" for instructions  
Enter SQL statements terminated with a ";"  
sqlite> **create table b (c1 integer primary key, c2 text UNIQUE);**  
sqlite> **insert or replace into b values (null,'test-1');**  
sqlite> **select last_insert_rowid();**  
1  
sqlite> **insert or replace into b values (null,'test-2');**  
sqlite> **select last_insert_rowid();**  
2  
sqlite> **insert or replace into b values (null,'test-1');**  
sqlite> **select last_insert_rowid();**  
3  
sqlite> **select * from b;**  
2|test-2  
3|test-1  

Обходной путь заключается в изменении определения столбца c2 следующим образом.

create table b (c1 integer primary key, c2 text UNIQUE ON CONFLICT IGNORE);

и удалить предложение «или заменить» из ваших вставок;

затем при тестировании после вставки вам нужно будет выполнить следующий sql: select last_insert_rowid(), changes();

sqlite> **create table b (c1 integer primary key, c2 text UNIQUE ON CONFLICT IGNORE);**  
sqlite> **insert into b values (null,'test-1');**  
sqlite> **select last_insert_rowid(), changes();**  
1|1  
sqlite> **insert into b values (null,'test-2');**  
sqlite> **select last_insert_rowid(), changes();**  
2|1  
sqlite> **insert into b values (null,'test-1');**  
sqlite> **select last_insert_rowid(), changes();**  
2|0  

Возвращаемое значение изменений после 3-й вставки будет уведомлением для вашего приложения о том, что вам нужно будет найти идентификатор строки «test-1», поскольку он уже был в файле. Конечно, если это многопользовательская система, вам также нужно будет обернуть все это в транзакцию.

person Noah    schedule 27.01.2011
comment
С insert or replace into b values (1,'test-1'); я не могу знать о (1, val), поэтому вместо этого я использовал null. Я получаю те же результаты, если пробую insert or replace into b (c2) values(val) и insert or replace into b (c2) select val с теми же результатами. Вот изображение (почему я не могу скопировать/вставить в/из sqlite-shell!) i.imgur.com /2a6IY.png - person ; 28.01.2011
comment
хм, так что я в основном проверяю изменения, и если это 1, я использую rowid, иначе мне нужен оператор выбора. Это очень похоже на мой ответ ниже. Хорошо, это приемлемое решение, +1. Я приму текущий ответ как принятый, потому что я успешно протестировал его под mysql. - person ; 28.01.2011
comment
вы знаете, что этот принятый ответ на самом деле не отвечает на ваш вопрос об исключении оператора select, когда он не нужен; - person Noah; 31.01.2011
comment
Боюсь, ваш ответ помогает, но не отвечает на вопрос. OP хочет избежать дополнительных запросов на выборку для получения идентификатора, вы предоставляете способ избежать этого только тогда, когда уникальное ограничение не не работает. См. также мой ответ stackoverflow.com/a/65869514/7015849 - person spawn; 24.01.2021

Я использую ниже в настоящее время

insert into tbl(c_name) select 'val' where not exists(select id from tbl where c_name ='val');
select id from tbl where c_name ='val';
person Community    schedule 27.01.2011

Под «они ДОЛЖНЫ быть уникальными» они означают, что вы уверены, что они уникальны, или что в результате вы хотите получить ошибку, если это не так? Если вы просто сделаете саму строку ключом в своей таблице, то я не понимаю, как 1 или 2 могут быть проблемой - вы получите ошибку по желанию в случае нежелательного дублирования, иначе правильный идентификатор. Может быть, вы можете уточнить свой вопрос с помощью небольшого примера кода SQL, который вы используете, рассматриваемой таблицы, какое поведение вы наблюдаете и какое поведение вы хотели бы вместо этого ...?

Отредактировано: спасибо за редактирование, но мне все еще неясно, какие проблемы создает SQL! Если ваша таблица взята, например:

CREATE TABLE Foo(
  theid INTEGER PRIMARY KEY AUTOINCREMENT,
  aword TEXT UNIQUE ABORT
  )

тогда любая попытка ВСТАВИТЬ повторяющееся слово потерпит неудачу (ключевое слово ABORT является необязательным, так как оно используется по умолчанию для UNIQUE) -- разве это не то, что вы хотите, учитывая, что вы говорите, что слова "ДОЛЖНЫ быть уникальными" ", т. е. ошибка, если нет?

person Alex Martelli    schedule 10.05.2009
comment
вместо ошибки я хотел бы, чтобы last_insert_rowid дал мне идентификатор (PK) текста. Пока замена изменяет идентификатор, который нарушает предыдущие записи, и ошибка не возвращает идентификатор строки. Похоже, мне нужно сделать это в два этапа? оператор выбора, затем оператор вставки, если он не существует - person ; 11.05.2009

Правильный ответ на ваш вопрос: это невозможно сделать в sqlite. Вы должны сделать дополнительный запрос на выборку. Цитируя документы для last_insert_rowid:

INSERT, завершившийся ошибкой из-за нарушения ограничения, не считается успешным INSERT и не изменяет значение, возвращаемое этой подпрограммой. Таким образом, INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK и INSERT OR ABORT не вносят изменений в возвращаемое значение этой подпрограммы, когда их вставка не удалась.

person spawn    schedule 24.01.2021