2

ストアド プロシージャに渡す値が列の値と異なる場合にのみ更新したい列があります。それが重要な場合、たまたま NVARCHAR(255) 列になります。

この値を毎回書き込むことの長所と短所は何ですか? 最初に値をチェックし、渡したものがデータベースにあるものと異なる場合にのみ書き込むことの長所と短所は何ですか?

書く前に比較をしている私の単純化された例:

-- @URL and @ContentName are parameters of the sproc    

SET @ExistingURL =
    (SELECT TOP 1 C.URL
    FROM Content C
    WHERE ContentName = @ContentName)

-- update only if the parameter and existing value are different
IF(@ExistingURL != @URL)
BEGIN
    UPDATE Content
    SET URL = @URL
    WHERE ContentName = @ContentName
END
4

3 に答える 3

2

ローカル変数に何も入れる必要なく、次のようにクエリを書き直すことができるようです。

UPDATE dbo.Content
  SET URL = @URL
  WHERE ContentName = @ContentName
  AND URL <> @URL;

それが一意であると仮定するとContentName、これは0または1行に影響します。URL のチェックを別のクエリにプルして、ロジックをより複雑にする必要はありません。「必要な場合にのみ書き込む」部分は、WHERE句によって処理されます。

さて、これを複数の列に拡張して更新する場合、求めていることはそれほど単純ではありません (そして、それは価値があるとは思いません)。たとえば、別の列 name がtitleあり、 という変数が入ってきたとし@titleます。URL変更された場合にのみ更新し、変更された場合にのみ更新するのが賢明であると予測しますtitle。理論的には、いくつかの方法でこれを達成できます (覚えておいてください、これはすべて、変数と列の両方が null 可能ではないことを前提としています。もしそうであれば、これらのアプローチは変更されず、構文が少しだけ変更されます)より困難):

  1. 2 つの独立した更新を実行する

    UPDATE dbo.Content SET URL = @URL
      WHERE ... -- actual where clause
      AND URL <> @URL;
    
    UPDATE dbo.Content SET title = @title
      WHERE ... -- same where clause
      AND title <> @title;
    
  2. 条件付きで更新

    UPDATE dbo.Content
      SET URL = CASE WHEN URL <> @URL THEN @URL ELSE URL END,
          title = CASE WHEN title <> @title THEN @title ELSE title END
    WHERE ... -- same where clause
    AND (URL <> @URL OR title <> @title);
    

とにかく行をロックして書き込みしているので、後者はこれを書くためのより自然な方法よりも優れたパフォーマンスを発揮しないと思います(ただし、Mikeからの承認以外に、その絶対確実な証拠はありません上記のコメント):

UPDATE dbo.Content
  SET URL = @URL, title = @title
WHERE ... -- same where clause
AND (URL <> @URL OR title <> @title);

つまり、実際に変更された値に関係なく、すべての値を更新するだけです。

于 2012-09-25T23:59:15.643 に答える
1

この問題を解決する簡単なパターンがあります。

update my_table set
my_col = 'newValue'
where id = 'someKey'
and my_col != 'newValue';

このクエリは、インデックス付きの読み取りのパフォーマンスを備えており、重要なことに、値を更新する必要がない場合、行をロックしません。

値の更新が必要な場合にのみ、where 句が行にヒットし、更新のために (短時間ではありますが) ロックします。

これを拡張して、同じロジックを使用して複数の列を処理できます。

update my_table set
my_col1 = 'newValue1',
my_col2 = 'newValue2'
where id = 'someKey'
and (my_col1 != 'newValue1'
    or my_col2 != 'newValue2');
于 2012-09-26T00:59:38.270 に答える
1

通常、書き込みには読み取りの 2 倍の時間がかかります。
書き込みはロックを設定するため、読み取りが保留される可能性があります。
トランザクション ログにエントリが配置されます。
その値にトリガーがある場合はどうなりますか。

練習問題として、アクティブなデータベースで <> をテストします。

テーブルがアクティブな場合、すべての行をロックすると大打撃を受ける可能性があります。
更新はロックを取得する必要があります。
他のクエリは、ロックを待機する必要があります (ダーティ リードを受け入れる場合を除く)。

大きなアクティブなテーブルの行の 10% だけを実際に更新し、<> をテストしないことですべてを盲目的に更新する場合、それは大ヒットです。

行の 99% を更新している場合は、小さなヒットになります。
<> のテストには時間がかかります。

チェックすることで小さな打撃を受ける可能性がありますが、チェックしないことであなた (および他の人) が大きな打撃を受ける可能性があります。

<> をチェックする更新の例

Update [docSVsys] set [ftsStatus] = 5 where [ftsStaus] <> 5 

ロックは、個々の列ではなく、行全体に適用されます。複数の値を更新している場合でも重要なのは、行の % です。

Update [docSVsys] set [ftsStatus] = 5, [wfID] = 3 
where [ftsStatus] <> 5 or [wfID] <> 3

値の 1 つだけが変更された場合、行の更新ロックを取得する必要があるため、両方の更新はヒットしません。

何時間も経って投稿された実際の声明。

アーロンが述べたように、左結合は何もしません。

現状では、最初のクエリは @URL または null を返します。
また、ContentName に基づいていません。

だから見ている

if(@ExistingURL != @URL)

これは、テーブル内の行にその値がない場合にのみ当てはまります。

一意の制約は、NULL を 1 つしか許可しないため、同じではありません。

あなたの質問がそうではないことに驚いています。このアップデートは失敗しますか?

于 2012-09-26T00:33:50.800 に答える