web-dev-qa-db-ja.com

存在しない場合は挿入します。

「新しいレコードを挿入する、または既存のレコードを更新するにはどうすればよいですか」という古典的な解決策はいくつかあると思いますが、SQLiteでそれらを機能させることはできません。

次のように定義されたテーブルがあります。

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

私がやりたいことは、一意の名前を持つレコードを追加することです。名前が既に存在する場合は、フィールドを変更します。

誰かがこれを行う方法を教えてもらえますか?

252
SparkyNZ

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列に副選択がある理由です。置き換えの場合、ステートメントはそれを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"));
304
janm

INSERT OR IGNOREコマンドに続けてUPDATEname__コマンドを使用する必要があります。次の例では、namename__が主キーです。

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

最初のコマンドはレコードを挿入します。レコードが存在する場合、既存の主キーとの競合によって発生したエラーは無視されます。

2番目のコマンドはレコードを更新します(これは間違いなく存在します)

75
moshik

テーブルに制約を設定して「 衝突 」を発生させる必要があります。それを置き換えて解決します。

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値が発生することを確認する必要があることも意味します。

61
gaspard

最初にそれを更新してください。 が行数に影響を与える= 0の場合は、それを挿入します。その最も簡単で、すべてに適していますRDBMS

32
Burçin

INSERT OR REPLACEは他のフィールド(TypeIDLevel)をデフォルト値に置き換えます。

INSERT OR REPLACE INTO book(id, name) VALUES(1001, 'Programming')

私はこれを使っています

INSERT OR IGNORE INTO book(id) VALUES(1001);
UPDATE book SET name = 'Programming' WHERE id = 1001;

また使用することができます

INSERT OR REPLACE INTO book (id, name) 
VALUES (1001, 'Programming',
  (SELECT typeid FROM book WHERE id = 1001),
  (SELECT level FROM book WHERE id = 1001),
)

しかし、私は最初の方法が読みやすいと思います

16
Steely Wing

主キーがない場合は、存在しない場合は挿入してから更新できます。これを使用する前に、テーブルに少なくとも1つのエントリが含まれている必要があります。

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

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 ...をしているなら、join構文でトークンONに関するパーサの曖昧さを解決するためにあなたのselectは少なくともWHERE trueを必要とします。

INSERT OR REPLACE...は、置き換える必要がある場合は新しいレコードを挿入する前にそのレコードを削除することに注意してください。外部キーのカスケードまたは他の削除トリガがある場合、これは悪くなる可能性があります。

3
ComradeJoecool

私はあなたが欲しいと思います UPSERT

その答えに追加のトリックのない "INSERT OR REPLACE"は、NULLまたは他のデフォルト値に指定しないフィールドをリセットします。 (このINSERT OR REPLACEの動作はUPDATEとは異なります。実際はINSERTであるため、INSERTとまったく同じです。実際の結果によって。)

提案されたUPSERT実装からのトリックは基本的にINSERT OR REPLACEを使用することですが、変更したくないフィールドの現在の値を取得するために埋め込みSELECT句を使用して、すべてのフィールドを指定します。

3
metamatt

PRIMARY KEY UNIQUEを完全に理解していないと、ここで予期しない動作が発生する可能性があることを指摘する価値があると思います。やり取りする。

例として、NAMEフィールドが現在とられていない場合にのみレコードを挿入したい場合、そしてそうであれば、制約例外を起動して通知します。その後 INSERT OR REPLACE は例外をスローせず、代わりにUNIQUE制約自体を解決します。競合するレコード(同じ名前を持つ既存のレコード)。 Gaspardの は、上の 彼の答え で本当によくこれを示しています。

制約例外を起動したい場合は、INSERTステートメントを使用し、別のUPDATEコマンドを使用する必要があります。名前がとられていないことがわかったら、レコードを更新します。

1
Matt Matthias