[sqlite] 다른 업데이트가 없으면 삽입?

고전적인 “새로운 레코드를 삽입하거나 이미 존재하는 경우 업데이트하는 방법”에 대한 몇 가지 “있을 것입니다”솔루션을 찾았지만 SQLite에서 작동하도록 할 수는 없습니다.

다음과 같이 정의 된 테이블이 있습니다.

CREATE TABLE Book
ID     INTEGER PRIMARY KEY AUTOINCREMENT,
Name   VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level  INTEGER,
Seen   INTEGER

내가하고 싶은 것은 고유 한 이름으로 레코드를 추가하는 것입니다. 이름이 이미 존재하면 필드를 수정하고 싶습니다.

누군가 제발이 방법을 말해 줄 수 있습니까?



답변

한 번 봐 가지고 http://sqlite.org/lang_conflict.html을 .

당신은 다음과 같은 것을 원합니다 :

insert or replace into Book (ID, Name, TypeID, Level, Seen) values
((select ID from Book where Name = "SearchName"), "SearchName", ...);

행이 이미 테이블에 존재하면 삽입 목록에없는 필드는 NULL로 설정됩니다. 이것이 ID컬럼에 대해 subselect가있는 이유입니다 . 대체 경우 명령문이이를 NULL로 설정 한 다음 새 ID가 할당됩니다.

이 방법은 대체 사례의 행이 있지만 삽입 사례의 경우 필드를 NULL로 설정하는 경우 특정 필드 값을 그대로 두려는 경우에도 사용할 수 있습니다.

예를 들어, Seen혼자 두고 싶다고 가정하면 :

insert or replace into Book (ID, Name, TypeID, Level, Seen) values (
   (select ID from Book where Name = "SearchName"),
   "SearchName",
    5,
    6,
    (select Seen from Book where Name = "SearchName"));


답변

INSERT OR IGNORE명령 뒤에 명령을 사용해야합니다 UPDATE. 다음 예 name에서 기본 키는 다음과 같습니다 .

INSERT OR IGNORE INTO my_table (name, age) VALUES ('Karen', 34)
UPDATE my_table SET age = 34 WHERE name='Karen'

첫 번째 명령은 레코드를 삽입합니다. 레코드가 존재하면 기존 기본 키와의 충돌로 인한 오류를 무시합니다.

두 번째 명령은 레코드를 업데이트합니다 (현재 존재 함)


답변

테이블에 제약 조건을 설정하여 ” 충돌 ” 을 트리거 한 다음 바꾸기를 수행하여 해결할 수 있습니다.

CREATE TABLE data   (id INTEGER PRIMARY KEY, event_id INTEGER, track_id INTEGER, value REAL);
CREATE UNIQUE INDEX data_idx ON data(event_id, track_id);

그런 다음 다음을 발행 할 수 있습니다.

INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 2, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 5);

“SELECT * FROM data”는 다음을 제공합니다.

2|2|2|3.0
3|1|2|5.0

REPLACE는 UPDATE가 아닌 DELETE 및 INSERT를 수행하므로 data.id는 “3”이며 “1”이 아닙니다. 또한 필요한 모든 열을 정의해야합니다. 그렇지 않으면 예기치 않은 NULL 값이 표시됩니다.


답변

먼저 업데이트하십시오. 경우 영향을받는 행 수 = 0 다음을 삽입합니다. 모든 RDBMS에 가장 쉽고 적합합니다 .


답변

INSERT OR REPLACE 다른 필드를 기본값으로 바꿉니다.

sqlite> CREATE TABLE Book (
  ID     INTEGER PRIMARY KEY AUTOINCREMENT,
  Name   TEXT,
  TypeID INTEGER,
  Level  INTEGER,
  Seen   INTEGER
);

sqlite> INSERT INTO Book VALUES (1001, 'C++', 10, 10, 0);
sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR REPLACE INTO Book(ID, Name) VALUES(1001, 'SQLite');

sqlite> SELECT * FROM Book;
1001|SQLite|||

다른 필드를 유지하려면

sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR IGNORE INTO Book(ID) VALUES(1001);
sqlite> UPDATE Book SET Name='SQLite' WHERE ID=1001;

sqlite> SELECT * FROM Book;
1001|SQLite|10|10|0

또는 UPSERT 사용 (구문이 버전 3.24.0 (2018-06-04) 인 SQLite에 추가됨)

INSERT INTO Book (ID, Name)
  VALUES (1001, 'SQLite')
  ON CONFLICT (ID) DO
  UPDATE SET Name=excluded.Name;

excluded.값과 동일한 접두사입니다 VALUES.


답변

Upsert 는 당신이 원하는 것입니다. UPSERT구문이 버전 3.24.0 (2018-06-04)으로 SQLite에 추가되었습니다.

CREATE TABLE phonebook2(
  name TEXT PRIMARY KEY,
  phonenumber TEXT,
  validDate DATE
);

INSERT INTO phonebook2(name,phonenumber,validDate)
  VALUES('Alice','704-555-1212','2018-05-08')
  ON CONFLICT(name) DO UPDATE SET
    phonenumber=excluded.phonenumber,
    validDate=excluded.validDate
  WHERE excluded.validDate>phonebook2.validDate;

이 시점에서 실제 단어 “UPSERT”는 upsert 구문의 일부가 아닙니다.

올바른 구문은

INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...

그리고 INSERT INTO SELECT ...선택을 하고 있다면 적어도 WHERE true토큰에 대한 파서 모호성을 해결 해야합니다.ON 조인 구문으로 합니다.

그 경고 INSERT OR REPLACE...당신이 외래 키 폭포 또는 기타 삭제 트리거가있는 경우 나쁜 될 수있는 대체 할 경우 새로운 하나를 삽입하기 전에 기록을 삭제합니다.


답변

기본 키가 없으면 존재하지 않는 경우 삽입 한 다음 업데이트를 수행 할 수 있습니다. 이를 사용하기 전에 테이블에 하나 이상의 항목이 포함되어 있어야합니다.

INSERT INTO Test
   (id, name)
   SELECT
      101 as id,
      'Bob' as name
   FROM Test
       WHERE NOT EXISTS(SELECT * FROM Test WHERE id = 101 and name = 'Bob') LIMIT 1;

Update Test SET id='101' WHERE name='Bob';