SQLの最終更新日列を維持する最良の方法は何ですか?
-
05-07-2019 - |
質問
更新または挿入された最後の時刻のtimedate列を持つデータベーステーブルがあるとします。どちらが望ましいでしょう:
- トリガーでフィールドを更新します。
- 挿入/更新を行うプログラムにフィールドを設定してもらいます。
最初のオプションは、再コンパイルする必要さえないので、最も簡単なようですが、それは大したことではありません。それ以外に、私は1つを他の上にする理由を考えるのに苦労しています。提案はありますか?
解決
データベースがフィールドを維持するため、最初のオプションはより堅牢です。これには、トリガーを使用するオーバーヘッドが発生する可能性があります。
将来、他のアプリが独自のインターフェイスを介してこのテーブルに書き込むことができる場合は、トリガーを使用して、他のどこでもそのロジックを繰り返さないようにします。
アプリがほとんどの場合、または他のアプリが同じデータレイヤーを介してデータベースにアクセスする場合、トリガーがデータレイヤー(SQL、ORM、ストアドプロシージャなど)。
もちろん、どちらの場合でも、タイムソース(アプリ、ユーザーのPC、SQLサーバー)が正確であることを確認する必要があります。
トリガーが好きではない理由について:
恐らく、彼らを悪夢と呼ぶことで発疹になったのでしょう。他のすべてと同様に、それらは適度に適切です。このような非常に単純なことのためにそれらを使用する場合、私は乗船することができます。
トリガーが多くの問題を引き起こし始めるのは、トリガーコードが複雑になる(そして高価になる)ときです。これらは、実行するすべての挿入/更新/削除クエリに対する隠れた税金です(トリガーの種類によって異なります)。その税が受け入れられるならば、彼らは仕事のための正しいツールでありえます。
他のヒント
3については言及していません。ストアドプロシージャを使用してテーブルを更新します。プロシージャは、必要に応じてタイムスタンプを設定できます。
おそらくそれはあなたにとって実行可能ではないかもしれませんが、私はそれが言及されるのを見ませんでした。
誰かがあなたのアプリ以外の何かを使ってテーブルを更新する場合に備えて、トリガーと言います>
トリガーを信頼するDBMSを使用している限り、常にトリガーオプションを使用します。これにより、DBMSができる限り多くのことを処理できます。これは通常、良いことです。
どのような状況でも、タイムスタンプ列に正しい値があることを確認してください。オーバーヘッドは無視できます。
トリガーに対する唯一のことは、移植性です。それが問題でなければ、どの方向に進むべきかという質問はないと思います。
私はすべてのストアドプロシージャの支持者です。更新プロシージャには、列のGETDATE()を含めることができます。
そして、この種の更新のトリガーは好きではありません。トリガーの可視性の欠如は混乱を招く傾向があります。
これは私にとってビジネスロジックのように聞こえます...私はこれをコードに入れることにもっと心がけます。データベースがデータのストレージを管理するようにします。これ以上でもそれ以下でもありません。
トリガーは祝福と呪いです。
祝福:それらを使用して、バックエンドシステムの知識や変更なしで、あらゆる種類のカスタム制約チェックとデータ管理を有効にできます。
呪い:あなたは背中の後ろで何が起こっているのか分かりません。元々予期されていなかったトランザクションに持ち込まれた追加オブジェクトによる同時実行の問題/デッドロック。セッション環境の変更、信頼できない行カウントなどのファントムの動作。条件の過度のトリガー。追加のホットスポット/パフォーマンスペナルティ。
この質問に対する回答(日付の更新は暗黙的に(トリガー)または明示的に(コード))通常、コンテキストに大きく影響します。たとえば、最終更新日を情報フィールドとして使用している場合、「ユーザー」が実際に行に顕著な変更を加えた場合と、ユーザーが気にしない内部マーカーの種類を単に更新する自動プロセスとのみ、変更したい場合があります。
変更の同期にトリガーを使用している場合、またはトリガーを実行しているコードを制御できない場合は、はるかに理にかなっています。
トリガーに関する私のアドバイスでは、注意して使用してください。ほとんどのシステムでは、操作と変更されたフィールドに基づいて実行をフィルタリングできます。 「前」トリガーと「後」トリガーを適切に使用すると、パフォーマンスに大きな影響を与える可能性があります。
最後に、いくつかのシステムは、複数の変更(トランザクション内で複数の行が影響を受ける)で単一のトリガーを実行できます。コードは、複数行への一括更新として自分自身を適用する準備をする必要があります。
通常はデータベース側で行うと言いますが、アプリケーションによって異なります。 LINQ-to-SQLを使用している場合、フィールドをTimestampとして設定し、DALで同時実行のためにTimestampフィールドを使用することができます。自動的に処理されるため、コードを繰り返す必要はありません。
ただし、DALを自分で作成する場合は、ユーザーインターフェイスの作成がはるかに柔軟になるため、データベース側でこれを処理する可能性が高くなります。ただし、これはストアドプロシージャで実行する可能性があります。 「公開」しているアクセスとテーブルのロックダウン-将来のアプリケーションがアクセスするために使用する必要のあるスタンドアロンコンポーネントをDALにすることを計画しない限り、道化師だけが来て、テーブルに直接書き込むことでストアドプロシージャをバイパスしたくないデータベース、この場合、DALに直接コーディングできます-もちろん、データベースにアクセスするすべてのユーザーがDALコンポーネントを介してそうすることを保証できる場合にのみ、これを行う必要があります。
" public"を許可する場合データベースにアクセスしてテーブルに挿入する場合は、トリガーを使用する必要があります。そうしないと、誰でもテーブル内の単一のフィールドを挿入/更新でき、更新されたフィールドは更新されません。
データベースで日付を維持します。つまり、トリガー、ストアドプロシージャなどです。ほとんどのデータベース駆動型アプリケーションでは、ユーザーアプリはビジネスユーザーがデータを取得する唯一の手段ではありません。 。レポートツール、抽出、ユーザーSQLなどがあります。DBAによって行われ、アプリケーションが日付を提供しない更新および修正もあります。
しかし、正直なところ、アプリケーションからそれを行わない一番の理由は、クライアントマシンの日付/時刻を制御できないことです。彼らは何かのトライアルライセンスからより多くの日数を得るためにそれをロールバックしているかもしれませんし、あなたのプログラムに悪いことをしたいかもしれません。
データベースがフィールドのデフォルト値をサポートしている場合、トリガーなしでこれを実行できます。たとえば、SQL Server 2005には、次のように作成されたフィールドを持つテーブルがあります。
create table dbo.Repository
(
...
last_updated datetime default getdate(),
...
)
その後、挿入コードはそのフィールドを挿入フィールドリストから除外します。
最初の挿入でのみ機能することを忘れていました-日付フィールドを更新し、更新されたレコードのコピーを履歴テーブルに入れるための更新トリガーもあります-これは投稿しますが...エディターコードでエラーが発生し続けます...
最後に:
create trigger dbo.Repository_Upd on dbo.Repository instead of update
as
--**************************************************************************
-- Trigger: Repository_Upd
-- Author: Ron Savage
-- Date: 09/28/2008
--
-- Description:
-- This trigger sets the last_updated and updated_by fields before the update
-- and puts a copy of the updated row into the Repository_History table.
--
-- Modification History:
-- Date Init Comment
-- 10/22/2008 RS Blocked .prm files from updating the history as they
-- get updated every time the cfg file is run.
-- 10/21/2008 RS Updated the insert into the history table to use the
-- d.last_updated field from the Repository table rather
-- than getdate() to avoid micro second differences.
-- 09/28/2008 RS Created.
--**************************************************************************
begin
--***********************************************************************
-- Update the record but fill in the updated_by, updated_system and
-- last_updated date with current information.
--***********************************************************************
update cr set
cr.filename = i.filename,
cr.created_by = i.created_by,
cr.created_system = i.created_system,
cr.create_date = i.create_date,
cr.updated_by = user,
cr.updated_system = host_name(),
cr.last_updated = getdate(),
cr.content = i.content
from
Repository cr
JOIN Inserted i
on (i.config_id = cr.config_id);
--***********************************************************************
-- Put a copy in the history table
--***********************************************************************
declare @extention varchar(3);
select @extention = lower(right(filename,3)) from Inserted;
if (@extention <> 'prm')
begin
Insert into Repository_History
select
i.config_id,
i.filename,
i.created_by,
i.created_system,
i.create_date,
user as updated_by,
host_name() as updated_system,
d.last_updated,
d.content
from
Inserted i
JOIN Repository d
on (d.config_id = i.config_id);
end
end
ロン