MySQL 8.0 リファレンスマニュアル


13.2.9 REPLACE ステートメント

REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    [(col_name [, col_name] ...)]
    { {VALUES | VALUE} (value_list) [, (value_list)] ...
      |
      VALUES row_constructor_list
    }

REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    SET assignment_list

REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    [(col_name [, col_name] ...)]
    {SELECT ... | TABLE table_name}

value:
    {expr | DEFAULT}

value_list:
    value [, value] ...

row_constructor_list:
    ROW(value_list)[, ROW(value_list)][, ...]

assignment:
    col_name = value

assignment_list:
    assignment [, assignment] ...

REPLACE は、INSERT とまったく同じように機能します。ただし、テーブル内の古い行に、PRIMARY KEY または UNIQUE インデックスに関して新しい行と同じ値が含まれている場合、その古い行は新しい行が挿入される前に削除されます。 セクション13.2.6「INSERT ステートメント」を参照してください。

REPLACE は、SQL 標準への MySQL 拡張です。 これは挿入を行うか、または削除と挿入を行います。 標準 SQL への別の MySQL 拡張 (挿入または更新を行います) については、セクション13.2.6.2「INSERT ... ON DUPLICATE KEY UPDATE ステートメント」を参照してください。

DELAYED の挿入および置換は、MySQL 5.6 で非推奨になりました。 MySQL 8.0 では、DELAYED はサポートされません。 サーバーは DELAYED キーワードを認識しますが、無視し、置換を遅延なし置換として処理し、ER_WARN_LEGACY_SYNTAX_CONVERTED 警告を生成: REPLACE DELAYED はサポートされなくなりました。 ステートメントは REPLACE に変換されました。 DELAYED キーワードは、将来のリリースで削除される予定です。

注記

REPLACE は、テーブルに PRIMARY KEY または UNIQUE インデックスがある場合にのみ意味を持ちます。 それ以外の場合は、新しい行が別の行と重複するかどうかを判断するために使用されるインデックスがないため、INSERT と同等になります。

すべてのカラムの値が REPLACE ステートメントで指定されている値から取得されます。 カラムがない場合は、INSERT での処理と同様に、そのカラムはそのデフォルト値に設定されます。 現在の行の値を参照し、それを新しい行で使用することはできません。 SET col_name = col_name + 1 などの代入を使用した場合、右側にあるカラム名への参照は DEFAULT(col_name) として処理されるため、この代入は SET col_name = DEFAULT(col_name) + 1 と同等です。

MySQL 8.0.19 以降では、REPLACEVALUES ROW() を使用して挿入を試みるカラム値を指定できます。

REPLACE を使用するには、このテーブルに対する INSERT 権限と DELETE 権限の両方が必要です。

生成されたカラムが明示的に置換される場合、許可される値は DEFAULT のみです。 生成されるカラムの詳細は、セクション13.1.20.8「CREATE TABLE および生成されるカラム」 を参照してください。

REPLACE では、パーティションまたはサブパーティション (あるいはその両方) のカンマ区切り名のリストを含む PARTITION キーワードを使用した明示的なパーティション選択がサポートされています。 INSERT と同様に、これらのいずれかのパーティションまたはサブパーティションに新しい行を挿入できない場合、REPLACE ステートメントはFound a row not matching the given partition set.エラーで失敗します。 詳細および例については、セクション24.5「パーティション選択」を参照してください。

REPLACE は、影響を受けた行数を示す数を返します。 これは、削除された行と挿入された行の合計です。 この数が単一行の REPLACE に対して 1 である場合は、行が挿入され、削除された行はありませんでした。 この数が 1 より大きい場合は、新しい行が挿入される前に 1 つ以上の古い行が削除されました。 テーブルに複数の一意のインデックスが存在するときに、新しい行が異なる一意のインデックス内の別の古い行の値を複製した場合は、単一行が複数の古い行を置き換えることがあります。

影響を受けた行数により、REPLACE が行を追加しただけか、または行の置き換えも行なったかを判定することが容易になります。その数が 1 (追加した) か、またはそれより大きい (置き換えた) かをチェックします。

C API を使用している場合は、mysql_affected_rows() 関数を使用して、影響を受けた行数を取得できます。

テーブルに置換して、サブクエリーの同じテーブルから選択することはできません。

MySQL は、REPLACE (および LOAD DATA ... REPLACE) に次のアルゴリズムを使用します。

  1. テーブルへの新しい行の挿入を試みます

  2. 主キーまたは一意のインデックスに関する重複キーエラーが発生したために挿入が失敗している間、次のことを行います。

    1. 重複キー値を含む競合している行をテーブルから削除します

    2. テーブルへの新しい行の挿入を再試行します

重複キーエラーが発生した場合、ストレージエンジンが削除と挿入ではなく、更新として REPLACE を実行する可能性がありますが、そのセマンティクスは同じです。 ストレージエンジンが Handler_xxx ステータス変数を増分する方法が異なる可能性がある以外、ユーザーに見える影響はありません。

REPLACE ... SELECT ステートメントの結果は SELECT からの行の順序に依存し、この順序は常に保証されるわけではないため、ソースとスレーブが相違するようにこれらのステートメントをロギングするときに可能です。 このため、REPLACE ... SELECT ステートメントにはステートメントベースレプリケーションに対して安全でないフラグが付けられます。このようなステートメントは、ステートメントベースモードの使用時にエラーログに警告を生成し、MIXED モードの使用時に行ベースの形式を使用してバイナリログに書き込まれます。 セクション17.2.1.1「ステートメントベースおよび行ベースレプリケーションのメリットとデメリット」も参照してください。

MySQL 8.0.19 以降では、INSERT と同様に、TABLE および SELECT with REPLACE がサポートされます。 詳細および例については、セクション13.2.6.1「INSERT ... SELECT ステートメント」を参照してください。

パーティション化されていない既存のテーブルをパーティション化に対応するように変更しているときや、すでにパーティション化されたテーブルのパーティション化を変更しているときに、そのテーブルの主キーの変更を検討する可能性があります (セクション24.6.1「パーティショニングキー、主キー、および一意キー」を参照してください)。 これを行うと、パーティション化されていないテーブルの主キーを変更した場合と同様に、REPLACE ステートメントの結果が影響を受ける可能性があります。 次の CREATE TABLE ステートメントによって作成されたテーブルを考えてみます。

CREATE TABLE test (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  data VARCHAR(64) DEFAULT NULL,
  ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);

このテーブルを作成し、mysql クライアントに示されているステートメントを実行すると、結果は次のようになります。

mysql> REPLACE INTO test VALUES (1, 'Old', '2014-08-20 18:47:00');
Query OK, 1 row affected (0.04 sec)

mysql> REPLACE INTO test VALUES (1, 'New', '2014-08-20 18:47:42');
Query OK, 2 rows affected (0.04 sec)

mysql> SELECT * FROM test;
+----+------+---------------------+
| id | data | ts                  |
+----+------+---------------------+
|  1 | New  | 2014-08-20 18:47:42 |
+----+------+---------------------+
1 row in set (0.00 sec)

ここで、次に示すように (強調表示されたテキスト) 主キーが 2 つのカラムになっている点を除き、最初のテーブルとほぼ同一の 2 番目のテーブルを作成します。

CREATE TABLE test2 (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  data VARCHAR(64) DEFAULT NULL,
  ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id, ts)
);

元の test テーブルに対して実行したのと同じ 2 つの REPLACE ステートメントを test2 に対して実行すると、異なる結果が得られます。

mysql> REPLACE INTO test2 VALUES (1, 'Old', '2014-08-20 18:47:00');
Query OK, 1 row affected (0.05 sec)

mysql> REPLACE INTO test2 VALUES (1, 'New', '2014-08-20 18:47:42');
Query OK, 1 row affected (0.06 sec)

mysql> SELECT * FROM test2;
+----+------+---------------------+
| id | data | ts                  |
+----+------+---------------------+
|  1 | Old  | 2014-08-20 18:47:00 |
|  1 | New  | 2014-08-20 18:47:42 |
+----+------+---------------------+
2 rows in set (0.00 sec)

これは、test2 に対して実行した場合は id カラムと ts カラムの両方の値が、置き換えられる行に対する既存の行の値に一致している必要があり、そうでないと行が挿入されるためです。


関連キーワード:  ステートメント, CREATE, REPLACE, TABLE, テーブル, DROP, カラム, 挿入, INSERT, サブクエリー