パフォーマンス関連の質問があります。マイケルという名のユーザーがいるとしましょう。次のクエリを実行します。
UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
同じ値に更新されている場合でも、クエリは実際に更新を実行しますか?もしそうなら、どうすればそれを防ぐことができますか?
パフォーマンス関連の質問があります。マイケルという名のユーザーがいるとしましょう。次のクエリを実行します。
UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
同じ値に更新されている場合でも、クエリは実際に更新を実行しますか?もしそうなら、どうすればそれを防ぐことができますか?
回答:
Postgres のMVCCモデルにより、SQLの規則に従って、は、句で除外されていないすべての行UPDATE
に対して新しい行バージョンを書き込みます。WHERE
これは、パフォーマンスに直接的または間接的に多かれ少なかれ実質的な影響を及ぼします。「空の更新」には、行ごとのコストが他の更新と同じです。他の更新と同様にトリガー(存在する場合)を起動し、WALログに記録する必要があり、テーブルを膨張させるデッド行を生成しVACUUM
、他の更新と同様に後の作業を増やします。
インデックスエントリとトーストの列関与列のどれもが変更されていないことができます同じまま、それはすべての更新された行に対して真です。関連:
そのような空の更新を除外することは、ほとんど常に良い考えです(実際に発生する可能性がある場合)。質問でテーブル定義を提供していません(常に良い考えです)。first_name
NULLであると仮定する必要があります(「名」にとっては驚くことではありません)。したがって、クエリはNULLセーフな比較を使用する必要があります。
UPDATE users
SET first_name = 'Michael'
WHERE id = 123
AND first_name IS DISTINCT FROM 'Michael';
first_name IS NULL
更新前の場合、テストはfirst_name <> 'Michael'
NULLと評価されるため、更新から行を除外します。卑劣なエラー。ただし、列が定義されている場合は、単純な同等性チェックを使用します。これは少し安価だからです。NOT NULL
関連:
Indexes entries and TOASTed columns where none of the involved columns are changed can stay the same
しかし、行の新しい場所を指すように更新する必要はありませんか?
rollback
、スナップショット処理、ロック管理、WAL、何ではない...
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'
NULL
@erwin
データベースの観点から
あなたの質問に対する答えはイエスです。更新が行われます。データベースは以前の値をチェックせず、新しい値を設定するだけです。
これはメモリ内で発生するため(コミットが発行された後にのみデータファイルに書き込まれます)、パフォーマンスは問題になりません。
ORMの観点から
通常、データベースの単一の行を表すオブジェクトがあります(それよりもはるかに複雑になる可能性がありますが、単純にしておきましょう)。このオブジェクトはメモリで(アプリサーバーレベルで)管理され、そのオブジェクトの最新のコミットバージョンのみが特定の時点で実際にデータベースに到達します。
それは異なる行動を説明するかもしれません。
さて、貨物船と3Dプリンターを比較しないようにしましょう。貨物船を使用して3Dプリンターを送信できるという事実は、それらの間に何らかの比較があるかもしれないという意味ではありません。
楽しい!
これにより、いくつかの概念が明らかになることを願っています。