マージクエリ
マージクエリは特殊なタイプのハイブリッドクエリです。構文はSQL 2003仕様で定義されていますが、標準構文をサポートするデータベースはほとんどありません。ただし、ほとんどのデータベースは、データベース固有の構文を使用した代替実装を提供しています。Drupalのマージクエリビルダーは、マージクエリの概念を構造化オブジェクトに抽象化し、各データベースに適した構文でコンパイルできるようにします。これらは、UPDATEとINSERTの組み合わせである「UPSERT」クエリと呼ばれることもあります。
一般的には、マージクエリは挿入クエリと更新クエリの組み合わせです。指定された条件が満たされた場合、たとえば指定された主キーを持つ行が既に存在する場合は、更新クエリが実行されます。存在しない場合は、挿入クエリが実行されます。最も一般的なケースでは、これは次と同等です:
if ($connection->query("SELECT COUNT(*) FROM {example} WHERE id = :id", [':id' => $id])->fetchField()) {
// Run an update using WHERE id = $id
}
else {
// Run an insert, inserting $id for id
}
実際の実装はデータベースによって大きく異なります。マージクエリは概念的に原子操作ですが、特定のデータベースの実装によっては、実際にはアトミックでない場合もあることに注意してください。たとえば、MySQLの実装は個別の原子クエリですが、上記の縮退したケースはそうではありません。
Mergeクエリの最も一般的なイディオムを以下に示します。
単純に設定する
$connection->merge('example')
->key('name', $name)
->fields([
'field1' => $value1,
'field2' => $value2,
])
->execute();
上記の例では、クエリに「example」テーブルで操作するよう指定しています。次に、$nameという値を持つ単一のキーフィールド'name'を指定します。次に、設定する値の配列を指定します。
「name」フィールドが$nameの値を持つ行が既に存在する場合、field1とfield2はその既存の行の対応する値に設定されます。そのような行が存在しない場合、nameが$name、field1が$value1、field2が$value2の値を持つ行が作成されます。したがって、クエリの最後では、行が存在したかどうかに関係なく、最終結果は同じになります。
条件付き設定
場合によっては、key()フィールドで定義されたレコードが既に存在するかどうかに応じて、値を異なる方法で設定する必要があるかもしれません。これを行うには2つの方法があります。
$connection->merge('example')
->insertFields([
'field1' => $value1,
'field2' => $value2,
])
->updateFields([
'field1' => $alternate1,
])
->key('name', $name)
->execute();
上記の例は最初の例と同じように動作しますが、レコードが既に存在して更新する場合、field1には$value1ではなく$alternate1が設定され、field2は影響を受けません。updateFields()メソッドは、値の単一の連想配列か、フィールドの配列と値の配列が同じ順序である2つの並行する数値配列のいずれかを受け入れます。
$connection->merge('example')
->key('name', $name)
->fields([
'field1' => $value1,
'field2' => $value2,
])
->expression('field1', 'field1 + :inc', [':inc' => 1])
->execute();
この例では、レコードが既に存在する場合、field1はその現在の値に1を加えたものに設定されます。これにより、特定のイベントが発生するたびにデータベースのカウンターを増やしたい「カウンタークエリ」に非常に便利です。field2は、レコードが存在するかどうかに関係なく同じ値になります。
expression()は、レコードが既に存在する場合に式に設定する各フィールドについて、複数回呼び出すことができることに注意してください。最初のパラメーターはフィールド、2番目はフィールドが設定される式を参照するSQLフラグメント、オプションの3番目のパラメーターは式に挿入するプレースホルダー値の配列です。
また、expression()で使用されるフィールドがfields()に既に存在する必要はありません。
上記のAPIを考えると、論理的に意味のないクエリを定義することは十分可能です。たとえば、フィールドが無視され、レコードが既に存在する場合は式に設定されるなどです。起こりうるエラーを最小限に抑えるため、次のルールが適用されます:
- フィールドにexpression()が設定されている場合、updateFields()よりも優先されます。
- updateFields()で値が指定されている場合、レコードが既に存在するときは、それらのフィールドのみが変更されます。updateFields()で指定されていないフィールドは影響を受けません。
意味のないクエリを定義することは依然として可能であることに注意してください。この場合の動作は未定義であるため、開発者は意味のないクエリを指定しないようにする必要があります。