UPDATE
主にパフォーマンスに関心があり、ほとんどがパフォーマンスに関心があることを知っていますが、仲間の "ORM"メンテナーとして、"changed"、"null"、および"default"の値を区別する問題について別の視点を教えてください。SQLには3つの異なる点がありますが、JavaとほとんどのORMには1つの点しかありません。
根拠をINSERT
声明に翻訳する
バッチ処理可能性と文のキャッシュ可能性を支持するあなたの議論は、文に対しても同じようにINSERT
文にも当てはまりますUPDATE
。ただし、INSERT
ステートメントの場合、ステートメントから列を省略することは、とは異なるセマンティクスを持ちUPDATE
ます。適用することを意味しますDEFAULT
。次の2つは意味的に同等です。
INSERT INTO t (a, b) VALUES (1, 2);
INSERT INTO t (a, b, c) VALUES (1, 2, DEFAULT);
これはには当てはまりません。UPDATE
最初の2つは意味的に同等であり、3つ目の意味はまったく異なります。
-- These are the same
UPDATE t SET a = 1, b = 2;
UPDATE t SET a = 1, b = 2, c = c;
-- This is different!
UPDATE t SET a = 1, b = 2, c = DEFAULT;
JDBCを含むほとんどのデータベースクライアントAPI、および結果としてJPAは、DEFAULT
式をバインド変数にバインドすることを許可しません。これは主に、サーバーもこれを許可しないためです。前述のバッチ処理可能性とステートメントのキャッシュ可能性の理由で同じSQLステートメントを再使用する場合は、両方のケースで次のステートメントを使用します((a, b, c)
すべての列がの場合t
)。
INSERT INTO t (a, b, c) VALUES (?, ?, ?);
また、c
は設定されていないnull
ため、Java を3番目のバインド変数にバインドすることになるでしょう。これは、多くのORMでもNULL
and DEFAULT
(jOOQ、たとえばここでは例外)を区別できないためです。彼らはJavaのみをnull
認識し、これがNULL
(未知の値のように)またはDEFAULT
(初期化されていない値のように)意味するかどうかを知りません。
多くの場合、この区別は重要ではありませんが、列cが次の機能のいずれかを使用している場合、ステートメントは単に間違っています。
- それは持っている
DEFAULT
句を
- トリガーによって生成される可能性があります
UPDATE
ステートメントに戻る
上記はすべてのデータベースに当てはまりますが、トリガーの問題はOracleデータベースにも当てはまることを保証できます。次のSQLを検討してください。
CREATE TABLE x (a INT PRIMARY KEY, b INT, c INT, d INT);
INSERT INTO x VALUES (1, 1, 1, 1);
CREATE OR REPLACE TRIGGER t
BEFORE UPDATE OF c, d
ON x
BEGIN
IF updating('c') THEN
dbms_output.put_line('Updating c');
END IF;
IF updating('d') THEN
dbms_output.put_line('Updating d');
END IF;
END;
/
SET SERVEROUTPUT ON
UPDATE x SET b = 1 WHERE a = 1;
UPDATE x SET c = 1 WHERE a = 1;
UPDATE x SET d = 1 WHERE a = 1;
UPDATE x SET b = 1, c = 1, d = 1 WHERE a = 1;
上記を実行すると、次の出力が表示されます。
table X created.
1 rows inserted.
TRIGGER T compiled
1 rows updated.
1 rows updated.
Updating c
1 rows updated.
Updating d
1 rows updated.
Updating c
Updating d
ご覧のとおり、すべての列を常に更新するステートメントは常にすべての列のトリガーを起動しますが、変更された列のみを更新するステートメントはそのような特定の変更をリッスンしているトリガーのみを起動します。
言い換えると:
説明しているHibernateの現在の動作は不完全であり、トリガー(およびおそらく他のツール)が存在する場合は間違っていると見なすこともできます。
個人的には、動的SQLの場合、クエリキャッシュ最適化の引数は過大評価されていると思います。確かに、このようなキャッシュにはさらにいくつかのクエリがあり、実行する解析作業がもう少しありますが、これは通常、動的UPDATE
ステートメントでは問題ではありませんSELECT
。
バッチ処理は確かに問題ですが、私の意見では、ステートメントがバッチ処理される可能性がわずかにあるという理由だけで、すべての列を更新するために単一の更新を正規化すべきではありません。ORMは、連続した同一ステートメントのサブバッチを収集し、「バッチ全体」の代わりにバッチ処理することができます(ORMが「changed」、「null」、および「default」の違いを追跡できる場合もあります)
UPDATE
と実質的に同等であるDELETE
+INSERT
(あなたが実際に新しい作成するためのV行のERSIONを)。オーバーヘッドは高く、特にインデックスを構成する列の多くが実際に更新され、 インデックスを表すために使用されるツリー(またはその他)が大幅に変更される場合、インデックスの数とともに増加します。更新されるのは関連する列の数ではなく、インデックスの列部分を更新するかどうかです。