319

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

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

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

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

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

4

9 に答える 9

355

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"));
于 2010-09-03T10:55:02.250 に答える
109

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'

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

2番目のコマンドは、レコードを更新します(現在は確実に存在します)

于 2013-12-01T09:48:18.067 に答える
73

「競合」をトリガーするためにテーブルに制約を設定する必要があります。その後、置換を実行して解決します。

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データ」はあなたに与えるでしょう:

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

REPLACEはUPDATEではなくDELETEとINSERTを実行するため、data.idは「1」ではなく「3」であることに注意してください。これは、必要なすべての列を定義する必要があることも意味します。そうしないと、予期しないNULL値が取得されます。

于 2011-01-27T10:54:28.540 に答える
40

まず更新します。影響を受ける行数=0の場合は、それを挿入します。最も簡単で、すべてのRDBMSに適しています。

于 2010-09-03T10:50:07.407 に答える
37

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

他のフィールドを保持したい場合

  • 方法1
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
  • 方法2

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

于 2014-05-15T09:12:52.217 に答える
28

アップサートはあなたが望むものです。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」はアップサート構文の一部ではないことに注意してください。

正しい構文は次のとおりです。

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

また、選択を行う場合はINSERT INTO SELECT ...、少なくとも結合構文を使用しWHERE trueてトークンに関するパーサーのあいまいさを解決する必要があります。ON

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

于 2018-10-31T16:42:29.457 に答える
6

主キーがない場合は、存在しない場合は挿入してから更新できます。これを使用する前に、テーブルに少なくとも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';
于 2016-01-01T16:21:28.450 に答える
4

UPSERTが欲しいと思います。

その回答に追加のトリックを含まない「INSERTORREPLACE」は、指定していないフィールドをNULLまたはその他のデフォルト値にリセットします。(INSERT OR REPLACEのこの動作はUPDATEとは異なります。実際にはINSERTであるため、INSERTとまったく同じです。ただし、必要なものがUPDATE-if-existの場合は、おそらくUPDATEセマンティクスが必要であり、実際の結果に不愉快に驚かれることでしょう。)

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

于 2012-08-02T23:15:36.367 に答える
1

PRIMARY KEYUNIQUEがどのように相互作用するかを完全に理解していない場合、ここで予期しない動作が発生する可能性があることを指摘する価値があると思います。

例として、 NAMEフィールドが現在取得されていない場合にのみレコードを挿入する場合、および取得されている場合は、制約例外を発生させて通知する場合、INSERT OR REPLACEは例外をスローせず、代わりに競合するレコード(同じNAMEを持つ既存のレコード)を置き換えることにより、 UNIQUE制約自体を解決します。Gaspardは、上記の彼の答えでこれを非常によく示しています。

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

于 2017-05-04T09:44:00.080 に答える