パフォーマンスに関する質問があります。たとえば、マイケルという名前のユーザーがいるとします。次のクエリを見てください。
UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
同じ値に更新されていても、クエリは実際に更新を実行しますか?もしそうなら、どうすればそれを防ぐことができますか?
Postgresの MVCCモデル のため、SQLのルールに従って、UPDATE
は every 行の新しい行バージョンを書き込みますWHERE
句で除外されていません。
これ does は、直接的または間接的に、パフォーマンスに多かれ少なかれ大きな影響を与えます。 「空の更新」の行あたりのコストは、他の更新と同じです。他の更新と同様に、トリガーが存在する場合はそれらをトリガーします WAL-logged である必要があり、テーブルを肥大化させ、後で他の更新と同様にVACUUM
にさらに多くの作業を引き起こします。 。
インデックスエントリと TOASTed 関係する列が変更されていない列 can は同じままですが、更新されたすべての行に当てはまります。関連:
そのような空の更新を除外することは、ほとんどの場合良い考えです(実際にそれが起こる可能性がある場合)。質問にテーブル定義を提供していません(これは常に良いアイデアです)。 first_name
はNULLであると想定する必要があります(これは「名」に意外なことではありません)。したがって、クエリでは NULL-safe比較 :
UPDATE users
SET first_name = 'Michael'
WHERE id = 123
AND first_name IS DISTINCT FROM 'Michael';
更新前にfirst_name IS NULL
の場合、first_name <> 'Michael'
だけを含むテストはNULLと評価され、そのため、更新から行が除外されます。卑劣なエラー。 If 列が defined NOT NULL
の場合、シンプルなただし、等価性チェックは少し安くなります。
関連:
Ruby on Railの提供するORMは、レコードを変更済み(または変更なし)としてマークし、必要または呼び出されたときに、データベースに変更を送信する遅延実行を提供します。
PostgreSQLはデータベースであり、ORMではありません。新しい値がクエリ内の更新された値と同じかどうかを確認するのに時間がかかると、パフォーマンスが低下します。
したがって、新しい値と同じかどうかに関係なく、値を更新します。
これを防止したい場合は、彼の回答で提案されているMax Vernonのようなコードを使用できます。
単にwhere
句に追加できます:
UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
AND (first_name <> 'Michael' OR first_name IS NULL);
first_name
と定義されている NOT NULL
、OR first_name IS NULL
パーツは削除できます。
状態:
(first_name <> 'Michael' OR first_name IS NULL)
(Erwinの答えで)よりエレガントに書くこともできます:
first_name IS DISTINCT FROM 'Michael'
データベースの観点から
あなたの質問への答えはイエスです。更新が行われます。データベースは以前の値をチェックせず、新しい値を設定するだけです。
これはメモリ内で発生するため(コミットが発行された後にのみデータファイルに書き込まれるため)、パフォーマンスは問題になりません。
ORMの観点から
通常、データベースの単一の行を表すオブジェクトがあります(それよりもはるかに複雑になる可能性がありますが、単純にしておくことにします)。このオブジェクトはメモリ内で(アプリサーバーレベルで)管理され、そのオブジェクトの最新のコミットバージョンのみが実際に特定の時点でデータベースに到達します。
それは異なる行動を説明するかもしれません。
さて、貨物船と3Dプリンターを比較しないでください。貨物船を使用して3Dプリンターを送信できるという事実は、それらの間に何らかの種類の比較があるかもしれないことを意味しません。
楽しい!
これでいくつかの概念が明確になったことを願っています。