INSERT 構文

1. INSERT ... SELECT 構文
2. INSERT DELAYED 構文
3. INSERT ... ON DUPLICATE KEY UPDATE 構文
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    VALUES ({expr | DEFAULT},...),(...),...
    [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

または:

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name
    SET col_name={expr | DEFAULT}, ...
    [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

または:

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    SELECT ...
    [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

INSERT は既存テーブルに新しい行を挿入します。INSERT ... VALUESINSERT ... SET 型のステートメントは、明示的に指定された値に基づいて行を挿入します。INSERT ... SELECT 型は別のテーブルから選択された行を挿入します。INSERT ... SELECT については 項1. 「INSERT ... SELECT 構文」 でさらに詳しく説明されています。

古い行に上書きする為に、INSERT の代わりに REPLACE を利用する事ができます。REPLACE は、古い行を複製する固有キー値を含む新しい行の取り扱いの中では INSERT IGNORE と同等になります。新しい行は捨てられるのではなく、古い行を置き換えるのに利用されます。詳しくは 項 「REPLACE 構文」 を参照してください。

tbl_name は行が挿入されるべきテーブルです。ステートメントが値を提供しなければいけないカラムは次のように指定する事ができます。

カラム値を与える方法はいくつかあります。

VALUES 構文を利用する INSERT ステートメントは複数行を挿入する事ができます。これをする為には、それぞれが括弧で囲まれカンマで区切られている、カラム値の複数リストを含んでください。例:

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

各行の値のリストは括弧で囲まれている必要があります。次のステートメントは、カラム名の数とリストの中の値の数が合わない為、不正となります。

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3,4,5,6,7,8,9);

INSERT の行に影響された値は mysql_affected_rows() C API 関数を利用して取得できます。詳しくは 項1. 「mysql_affected_rows()」 を参照してください。

もし INSERT ... VALUES ステートメントを複数値リストか INSERT ... SELECT と共に利用すると、そのステートメントはこのフォーマットで文字列情報を戻します。

Records: 100 Duplicates: 0 Warnings: 0

Records はステートメントによって生成された行数を指示します。(Duplicates がゼロ以外の数値になりえる為、実際に挿入された行数でなければいけないという訳ではありません。)既存の固有インデックス値を複製するので、Duplicates は挿入できなかった行数を指示します。Warnings は、何らかの形で問題があった、カラム値挿入の試みの回数を指示します。警告は次のような条件時に発生します。

もしC API を利用していれば、mysql_info() 関数を呼び出す事で、情報文字列を得ることができます。詳しくは 項35. 「mysql_info()」 を参照してください。

もし AUTO_INCREMENT カラムを持つテーブルに INSERT が行を挿入すれば、SQL LAST_INSERT_ID() 関数を利用する事でそのカラムに利用される値を見つける事ができます。C API の内部から、mysql_insert_id() 関数を利用してください。しかし、2つの関数がいつでも全く同じ働きをする訳ではない事に注意してください。AUTO_INCREMENT カラムに関しての、INSERT ステートメントの動作の更なる情報は 項 「情報関数」 と 項37. 「mysql_insert_id()」 で紹介されています。

INSERT ステートメントは次の修飾因子をサポートします。

1. INSERT ... SELECT 構文

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    SELECT ...
    [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

INSERT ... SELECT を利用すると、1つ、または複数のテーブルから多くの行をすばやく挿入する事ができます。例:

INSERT INTO tbl_temp2 (fld_id)
  SELECT tbl_temp1.fld_order_id
  FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;

次の条件は INSERT ... SELECT ステートメントを保持します。

  • 複製キー違反を引き起こす行を無視する為に IGNORE を指定してください。

  • DELAYEDINSERT ... SELECT と共に無視されます。

  • INSERT ステートメントの対象テーブルはクエリの SELECT 部のFROM 条項内に現れます。(これは MySQL の古いバージョンでは不可能でした。)この場合、MySQL は行を保持する為に SELECT からテンポラリ テーブルを作成し、そして対象テーブルにそれらの行を挿入します。

  • AUTO_INCREMENT カラムは通常通り機能します。

  • バイナリ ログが元テーブルを再作成する為に利用できる事を保障する為に、MySQL は INSERT ... SELECT ステートメントへの並列挿入を許可しません。

  • 現在は、サブクエリの中で1つのテーブルに挿入し、同じテーブルから選択する事はできません。

  • SELECTINSERT が同じテーブルを参照した時に、曖昧なカラム参照の問題を防ぐ為に、SELECT 部分で利用される各テーブルに固有のエイリアスを与え、その部分の中でのカラム名を適切なエイリアスに限定してください。

ON DUPLICATE KEY UPDATE の値部分の中で、SELECT 部分の中で GROUP BY を利用しない限り、別のテーブル内でカラムの参照をする事ができます。値の部分で非固有カラム名を指定しなければいけない、という副作用が1つあります。

2. INSERT DELAYED 構文

INSERT DELAYED ...

INSERT ステートメントの DELAYED オプションは、もし INSERT が完了するのを待つ事ができない、または待つ必要がないクライアントを持っている場合に大変有効となる、スタンダード SQL の MySQL 拡張子です。これは、MySQL をログに利用し、完了までに長時間かかる SELECTUPDATE ステートメントを定期的に起動させる時によく起こる状態です。

クライアントが INSERT DELAYED を利用する時、サーバからすぐに OK が出て、テーブルが別のスレッドによって使用中でなければ行が挿入される為にキューを作ります。

INSERT DELAYED を利用する事のそれ以外の大きな利益は、たくさんのクライアントからの挿入は一緒にまとめられ、ひとつのブロックに書き込まれると言う事です。これは、別々の挿入を何度も行うよりも早く機能します。

INSERT DELAYED は、もしテーブルが他の形で利用されていないのであれば、通常の INSERT よりも遅いという事に注意してください。また、サーバには、遅れている行を持つ各テーブルに別々のスレッドを扱う為の、追加オーバーヘッドもあります。これは、本当に INSERT DELAYED が必要だという事が確実な時だけ利用するべきであるという事を意味します。

キューを作った行は、テーブルに挿入されるまでメモリ内だけで保持されます。これは、もしmysqld を強制的に終了させたり (例えば、kill -9 を利用して)、mysqld が突然停止してしまったりすると、ディスクに書き込まれる前のキューを作った行は全て失われてしまう という事を意味します。

DELAYED の利用に関しては、いくつかの制限があります。

  • INSERT DELAYEDMyISAMMEMORY、そして ARCHIVE テーブルとのみ機能します。項13.4. 「MyISAM ストレージエンジン」、項13.7. 「MEMORY (HEAP) ストレージエンジン」、そして 項13.10. 「ARCHIVE ストレージエンジン」 を参照してください。

    もしデータファイル中にフリー ブロックがなければ、MyISAM テーブルには並列 SELECTINSERT ステートメントがサポートされます。これらの条件下では、INSERT DELAYEDMyISAM と一緒に利用しなければいけない事はほとんどありません。

  • INSERT DELAYED は、値リストを指定する INSERT ステートメントにだけ利用されなければいけません。サーバは、INSERT ... SELECTINSERT ... ON DUPLICATE KEY UPDATE に対して DELAYED を無視します。

  • INSERT DELAYED ステートメントがすぐに返されるので、そのステートメントが生成するであろう AUTO_INCREMENT 値を得る為に、行が挿入される前に、LAST_INSERT_ID() を利用する事はできません。

  • DELAYED 行は、実際に挿入されるまでは SELECT ステートメントには見えません。

  • DELAYED は、スレーブにマスタとは異なるデータを持たせる事があるので、スレーブ複製サーバ上では無視されます。

  • テーブルが書き込みロックされ、ALTER TABLE がテーブル構造を変更するのに利用されると、保留中の INSERT DELAYED ステートメントは失われてしまいます。

  • INSERT DELAYED は画面をサポートしません。

次に、INSERTREPLACEDELAYED を利用した時に何が起こるかを詳しく説明しています。この説明の中では、「スレッド」 は INSERT DELAYED ステートメントを受け取ったスレッドで、「ハンドラ」 は特定のテーブルの為に全ての INSERT DELAYED ステートメントを扱うスレッドを表しています。

  • スレッドが DELAYED ステートメントをテーブルに実行した時、もし同じようなハンドラが既に存在していなければ、全ての DELAYED ステートメントをテーブルに生成する為にハンドラ スレッドが作成されます。

  • スレッドは、ハンドラが以前に DELAYED ロックを習得したかどうかを確認します。もし習得していなければ、ハンドラ スレッドに対して習得するよう命令します。もし他のスレッドが READWRITE ロックをテーブル上に持っていても、DELAYED ロックを得る事ができます。しかし、ハンドラはテーブル構造が最新であるかどうかを確認する為に、全ての ALTER TABLE ロックや FLUSH TABLES ステートメントが終了するのを待ちます。

  • スレッドは INSERT ステートメントを実行しますが、行をテーブルに書き込む代わりに、ハンドラ スレッドに管理されているキューに最終行のコピーを置きます。構文エラーは全てスレッドに見つけられ、クライアント プログラムにリポートされます。

  • クライアントは、挿入操作が完了する前に INSERT が返る為、複製行の数や、結果として生じる行の AUTO_INCREMENT 値をサーバから得る事ができません。(もし C API を利用すると、同じ理由で mysql_info() 関数からは意味のある答えが返りません。)

  • 行がテーブルに挿入された時、バイナリ ログはハンドラ スレッドによって更新されます。複合行挿入の場合、最初の行が挿入された時にバイナリ ログが更新されます。

  • delayed_insert_limit 行が書かれる度に、ハンドラはまだ保留中の SELECT ステートメントがないかどうかを確認します。もしあれば、続ける前にそれらを実行させます。

  • ハンドラのキューに行が無くなると、テーブルのロックは外されます。もし新しい INSERT DELAYED ステートメントが delayed_insert_timeout 秒以内に受信されたら、ハンドラは終了します。

  • もし delayed_queue_size 以上の行が、特定のハンドラ キューの中で保留中だったら、 INSERT DELAYED をリクエストしているスレッドは、キューの中にスペースができるまで待ちます。これは、遅れたメモリのキューの為に mysqld が全てのメモリを使わない事を保障する為に行われます。

  • ハンドラスレッドは、Command カラム内の delayed_insert と共に、MySQL プロセス リスト内に現れます。これは、もし FLUSH TABLES ステートメントを実行したり、KILL thread_id を利用したりすると中止されます。しかし、終了する前にまずテーブル内でキューを作っている全ての行を格納します。この最中は、別のスレッドから新しい INSERT ステートメントを受け入れません。もしこの後に INSERT DELAYED ステートメントを実行すると、新しいハンドラ スレッドが作成されます。

    もし起動中の INSERT DELAYED ハンドラがあったら、INSERT DELAYED ステートメントは通常の INSERT ステートメントより高い優先権を持つという事を意味します。その他の更新ステートメントは、INSERT DELAYED キューが空になるか、誰かがハンドラスレッドを終了させるか (KILL thread_id を利用して)、誰かが FLUSH TABLES を実行するまで待たなければいけません。

  • 次の状態変数は INSERT DELAYED ステートメントの情報を提供します。

    状態変数 意味
    Delayed_insert_threads ハンドラ スレッド数
    Delayed_writes INSERT DELAYED で書かれた行数
    Not_flushed_delayed_rows 書き込みを待つ行数

    SHOW STATUS ステートメントか、mysqladmin extended-status コマンドを実行する事でこれらの変数を見る事ができます。

3. INSERT ... ON DUPLICATE KEY UPDATE 構文

もし ON DUPLICATE KEY UPDATE を指定し、UNIQUE インデックスか PRIMARY KEY 内で複製値を引き起こす行が挿入されると、古い行の UPDATE が実行されます。例えば、もしカラム aUNIQUE として宣言され、それが値 1 を含んでいたら、次の2つのステートメントは同一効果を持ちます。

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE table SET c=c+1 WHERE a=1;

もしその行が新しいレコードとして挿入されると、行に影響される値は1となり、もし既存レコードが更新されると2になります。

もしカラム b も固有であれば、INSERT は代わりにこの UPDATE ステートメントと同等になります。

UPDATE table SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;

もし a=1 OR b=2 がいくつかの行とマッチすれば、1つの 行だけが更新されます。通常、複数の固有インデックスを持つテーブル上で ON DUPLICATE KEY 条項を利用するのは避けるべきです。

INSERT ... UPDATE ステートメントの INSERT 部分からカラム値を参照する為に、UPDATE 条項の中で VALUES(col_name) 関数を利用する事ができます。言い換えると、UPDATE 条項の中の VALUES(col_name) は、挿入される col_name の値を参照し、複製キーの矛盾は起きないという事です。この関数は、時に複合行挿入をする時に有効です。VALUES() 関数は、INSERT ... UPDATE ステートメントの中でだけ意味を持ち、そうでなければ NULL を返します。例:

INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
  ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

そのステートメントは次の2つと同一です。

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=3;
INSERT INTO table (a,b,c) VALUES (4,5,6)
  ON DUPLICATE KEY UPDATE c=9;

もしテーブルが AUTO_INCREMENT カラムを含み INSERT ... UPDATE が行を挿入すると、LAST_INSERT_ID() 関数は AUTO_INCREMENT 値を返します。もしステートメントが代わりに行を更新すると、LAST_INSERT_ID() は無意味になります。しかし、LAST_INSERT_ID(expr) を利用する事でこれに対処する事ができます。idAUTO_INCREMENT カラムだと仮定してください。LAST_INSERT_ID() が更新に意味を持つようにするには、次のように行を挿入してください。

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;

ON DUPLICATE KEY UPDATE を利用する時は DELAYED オプションは無視されます。