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


275

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

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

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

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

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


3
"삽입 또는 교체"는 "삽입 또는 업데이트"와 완전히 다릅니다
Fattie

답변:


320

한 번 봐 가지고 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"));

109
잘못된 "삽입 또는 교체"가 "삽입 또는 업데이트"와 다릅니다. 유효한 답변은 stackoverflow.com/questions/418898/…
rds

12
@rds 아니요,이 질문에 "필드 수정"이라고 표시되어 있고 기본 키가 열 목록의 일부가 아니지만 다른 모든 필드가 있기 때문에 잘못된 것이 아닙니다. 모든 필드 값을 바꾸지 않는 코너 케이스가 있거나 기본 키로 혼란스러워하는 경우 다른 작업을 수행해야합니다. 새로운 필드의 완전한 세트가 있다면이 방법은 괜찮습니다. 내가 볼 수없는 특정한 문제가 있습니까?
janm

9
모든 필드의 모든 새로운 값을 알고 있으면 유효합니다. 예를 들어, 사용자 업데이트 만이 Level방법을 수행 할 수 없습니다.
rds

4
맞습니다 . 다른 답변은 관련이 있지만이 방법은 모든 필드를 업데이트하는 데 유효합니다 (키 제외).
Ярослав Рахматуллин

2
예 이것은 완전하지 않습니다. 충돌에서 단일 열 값을 새 열에서 업데이트하려고하면 어떻게됩니까? 위의 경우 다른 모든 데이터는 올바르지 않은 새 데이터로 대체됩니다.
Mrug

85

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'

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

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


6
어느 순간에 무시됩니까? 이름과 나이가 모두 같은 경우
mou

이것이 해결책이 될 것입니다 ... 삽입시 트리거를 사용하면 매번 허용되는 응답이 발생합니다. 이 작업은 수행하지 않으며 업데이트 만 수행합니다
PodTech.io

1
이름만을 기준으로 무시합니다. "이름"열만 기본 키임을 기억하십시오.
Gabriel Ferrer

3
레코드가 새로운 경우 업데이트가 필요하지 않지만 어쨌든 실행되어 성능이 저하됩니까?
MarcG

준비된 문장을 실행하는 방법?
prashant

65

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

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 값이 표시됩니다.


33

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


11
데이터베이스에 관계없이 올바른 격리 수준의 트랜잭션에는 두 가지 작업이 문제가되지 않습니다.
janm

5
Insert or Replace정말 더 바람직합니다.
MPelletier

1
IT 문서에 더 많은 예제가 포함되기를 바랍니다. 아래에서 이것을 시도했지만 작동하지 않습니다 (구문이 분명히 잘못되었습니다). 그것이 무엇인지에 대한 아이디어가 있습니까? INSERT INTO Book (이름, TypeID, Level, Seen) VALUES ( '슈퍼맨', '2', '14', '0') 충돌 대체 도서 (Name, TypeID, Level, Seen) VALUES ( '슈퍼맨', ' 2 ','14 ','0 ')
SparkyNZ

6
또한 행 값이 정확히 동일한 경우 영향을받는 행 수는 0이되고 새로운 중복 행이 작성됩니다.
barkside

2
+1, INSERT OR REPLACE 는 충돌시 원래 행을 삭제하고 모든 열을 설정하지 않으면 원래 값을 잃게됩니다
Pavel Machyniak

20

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.


16

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...당신이 외래 키 폭포 또는 기타 삭제 트리거가있는 경우 나쁜 될 수있는 대체 할 경우 새로운 하나를 삽입하기 전에 기록을 삭제합니다.


그것은 문서에 존재하고 나는 3.25.1 인 sqlite의 최신 버전을 가지고 있지만 그것은 나를 위해 작동하지 않습니다
Bentaiba Miled Basma

이 두 쿼리를 그대로 사용 하시겠습니까?
ComradeJoecool

Ubuntu 18.04는 3.25가 아닌 SQLite3 v3.22 와 함께 제공 되므로 UPSERT구문을 지원하지 않습니다 .
starbeamrainbowlabs

기능의 참조 버전에 감사드립니다!
Matteo

6

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

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';

이것은 중복 항목을 만들지 않고 나를 위해 일한 유일한 솔루션입니다. 아마도 사용중인 테이블에 기본 키가 없으며 기본값이없는 2 개의 열이 있기 때문일 수 있습니다. 약간 긴 솔루션이지만 작업을 올바르게 수행하고 예상대로 작동합니다.
인바 로즈

4

나는 당신이 UPSERT 를 원한다고 생각합니다 .

해당 답변에 추가 속임수가없는 "INSERT OR REPLACE"는 지정하지 않은 필드를 NULL 또는 다른 기본값으로 재설정합니다. (INSERT OR REPLACE의이 동작은 UPDATE와는 다릅니다. 실제로 INSERT이기 때문에 INSERT와 똑같습니다. 그러나 원하는 것이 UPDATE- 존재하는 경우 UPDATE 의미를 원할 수 있으며 실제 결과에 의해 불쾌하게 놀라게됩니다.)

제안 된 UPSERT 구현의 속임수는 기본적으로 INSERT OR REPLACE를 사용하지만 임베드 된 SELECT 절을 사용하여 모든 필드를 지정하여 변경하지 않으려는 필드의 현재 값을 검색합니다.


1

기본 키고유 방식을 완전히 이해하지 못하면 여기에 예기치 않은 동작이있을 수 있다고 지적 할 가치가 있다고 생각합니다. 상호 작용 .

예를 들어, NAME 필드를 현재 가져 오지 않은 경우에만 레코드를 삽입하려는 경우 레코드 예외를 발생 시키려면 INSERT OR REPLACE 가 발생하지 않고 대신 예외가 발생합니다. 충돌 레코드 (기존 레코드가 동일한 NAME ) 를 대체 하여 UNIQUE 제한 조건 자체를 해결하십시오 . Gaspard그의 대답 에서 이것을 잘 보여줍니다. 위의 .

제약 조건 예외를 발생 시키려면 INSERT 문 을 사용 하고 이름을 가져 오지 않은 것을 알고 나면 별도의 UPDATE 명령을 사용하여 레코드를 업데이트해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.