10

レコードを更新して既存のレコードを取得し、フィールドをループして変更をチェックし、変更されたフィールドのみを更新クエリに入れるときに、サーバー時間を費やす価値があるかどうか疑問に思っています。(MySQL と PHP を使用しています。)

これを行う主な理由は、変更ログの目的で更新クエリのサイズを小さくすることです。通常、クエリには 15 のフィールドがありますが、実際に変更されるのは 2 つのフィールドだけです。このクエリは、変更されたフィールドのみが含まれるため、ログ記録にも使用できるため、解析が容易になります。

私の懸念は、既存のレコードを取得するのにかかる時間です。

または、更新されたフィールドを MySQL から取得する方法はありますか?

4

3 に答える 3

3

変更する価値はあると思いますが、おそらく挿入前に選択する価値はありません。

変更されたフィールドのみを更新します。これは、activerecord パターンに従う DbEntity クラスの操作の一部です。現在のレコードと元のレコードを保持しているため、これを行うのに余分な費用はかかりません。レコードがロードされるたびにコピーするだけです。

理由は簡潔であり、実際にはパフォーマンスではありません。また、更新されたフィールドの古い値に where 句を追加して、適切なエラーをスローすることにより、同時変更を確認できます。

書き込み/更新メソッド:

$s1 = "";

foreach ($this->record as $key => $value)
{
    // only update fields that have been changed
    if ($value != $this->orig_record[$key])
    {
        $s1 .= $comma."`$key`='".mysql_real_escape_string($value)."'";
        $comma = ", ";
    }
}

$query = "UPDATE ".$this->table." SET $s1 where {$this->id_field}='".$this->get_keyfield()."'";
$query .= $this->extra_sql_update;
mysql_query($query);

$ar = mysql_affected_rows();
//
// the number of affected rows is actually those changed by the update operation, which will 
// either be zero, or 1. If the query affects more than one row then we have a problem.
if ($ar < 0 || $ar > 1)
{
    cbf_error("cbf_dbentity: {$this->table} :: only one row (not $ar) must be affected by an insert operation. $query",
      E_USER_ERROR);
}
else
{
    $new_id = $this->get_keyfield();

    GlobalEventBus::notify_all(new AuditLogSQL($this->table, "update", $query));

}

$this->orig_record = Array();

foreach ($this->record as $key => $value)
    $this->orig_record[$key] = $value;


//
// sanity check - ensure that what we have just written is actually there.

$this->load($new_id);

foreach ($this->orig_record as $key => $value)
    if (trim($this->record[$key]) != trim($value) 
        && (!$this->record[$key] == "0" && $value=""))
        cbf_error("cbf_dbentity: {$this->table} :: record differs during write after reload: field $key was \"$value\", after write it is now \"".
              $this->record[$key]."\"",E_USER_ERROR);

ロード方法で

$this->orig_record = Array();
foreach ($this->record as $key => $value)
    $this->orig_record[$key] = $value;
于 2009-01-20T00:06:05.893 に答える
2

最も基本的なレベルでは、私があなたの質問を正しく読んでいれば、実際に変更していないレコードの一部を別のユーザーが既に更新している場合に備えて、レコード全体をやみくもに更新したくないのが一般的です。やみくもに不必要に彼らの更新を元に戻すでしょう。

現在のアルゴリズムがダーティ ライトにつながる可能性があると思います。更新のために現在のデータを 1 回読み取り、メモリ内での更新を許可してから、レコードを再度読み取って、どのフィールドが更新されたかを把握できるようにします。別のユーザーがあなたの背後でそのレコードを更新し、そのフィールドを更新したのはあなたであるとアルゴリズムに信じ込ませたらどうなるでしょうか? ただし、主に、1 回の更新を実行するためにすべてのレコードを 2 回読み取る必要はありません。

データが競合することがあまりない場合は、楽観的ロックを実装することを選択しなくても、楽観的ロックについて読むと役立つ場合があります。

ここでは、update-timestamp またはインクリメンタル update-number 列をテーブルに追加する方法を 1 つ実装しました。次に、サンドボックス/メモリで、変更したフィールド (古い値/新しい値) を追跡し、それらのフィールドのそのレコードの更新 SQL を自由に発行できます。「UPDATE...WHERE UPDATENUM=the-original-number」 " (または WHERE UPDATETS=the-original-timestamp)、更新 SQL が UPDATENUM または UPDATETS も適宜インクリメントするようにします。その更新 SQL によって影響を受けるレコードが 0 の場合、他の誰かがバックグラウンドでそのレコードを既に変更しており、競合が発生していることがわかります。しかし、少なくとも他の誰かの変更を上書きしていないので、新しいデータを再読み込みするか、ユーザーに競合を解決してもらうことができます。

于 2009-01-20T00:37:30.353 に答える
1

アプリケーションで最も遅いポイントは常にデータベースへのアクセスになるため、それを高速化できるのであれば、それは良い考えです。とはいえ、項目が更新されたかどうかをプログラムでチェックする価値があるかどうかは、データベースとレコードの大きさ、およびそれらがどれだけ大きくなる可能性があるかによって異なります。データベースが小さく、アクセスがすでに非常に高速である場合は、時間の価値がない可能性があります。しかし、速度を向上させることができ、それによってロギングに追加の利点が得られる場合は、それを選択してください。

于 2009-01-20T00:04:46.833 に答える