8

簡略化したテーブルは次のようになります。

create table Test
(
 ValidFrom date not null,
 ValidTo date not null,
 check (ValidTo > ValidFrom)
)

既存の日付範囲と重複する値の挿入を防ぐトリガーを作成したいと思います。私は次のようなトリガーを作成しました:

create trigger Trigger_Test
on Test
for insert
as
begin
 if exists(
  select *
  from Test t
   join inserted i
   on ((i.ValidTo >= t.ValidFrom) and (i.ValidFrom <= t.ValidTo))
 )
 begin
  raiserror (N'Overlapping range.', 16, 1);
  rollback transaction;
  return
 end;
end

しかし、新しく挿入されたレコードは、テストとトリガー内に挿入された両方のテーブルの一部であるため、機能しません。したがって、挿入されたテーブルの新しいレコードは、常にテストテーブルでそれ自体に結合されます。トリガーは常に変換を元に戻します。

新しいレコードと既存のレコードを区別できません。したがって、同じ日付範囲を除外すると、テーブルにまったく同じ範囲を複数挿入できるようになります。

主な質問は

exists()次のように、ステートメントから新しく挿入されたレコードを除外するために使用できるID列をテストテーブルに追加せずに、期待どおりに機能するトリガーを作成することは可能ですか?

create trigger Trigger_Test
on Test
for insert
as
begin
 if exists(
  select *
  from Test t
   join inserted i
   on (
    i.ID <> t.ID and /* exclude myself out */
    i.ValidTo >= t.ValidFrom and i.ValidFrom <=t.ValidTo
   )
 )
 begin
  raiserror (N'Overlapping range.', 16, 1);
  rollback transaction;
  return
 end;
end

重要アイデンティティなしでは不可能なことが唯一の答えである場合は、その理由を合理的な説明とともに提示することを歓迎します。

4

2 に答える 2

5

私はこれがすでに答えられていることを知っていますが、私は最近この問題に取り組み、うまくいくものを思いつきました(そして挿入された各行に対してシングルトンシークを実行するとうまく機能します)。この記事の例を参照してください:http: //michaeljswart.com/2011/06/enforcing-business-rules-vs-avoiding-triggers-which-is-better/

(そしてそれはアイデンティティ列を利用しません)

于 2011-06-17T12:52:17.833 に答える
3

2つの小さな変更で、すべてが正常に機能するはずです。

まず、トリガーにwhere句を追加して、重複レコードを結合から除外します。次に、挿入されたレコードをそれら自体と比較しません。

select *
  from testdatetrigger t
   join inserted i
   on ((i.ValidTo >= t.ValidFrom) and (i.ValidFrom <= t.ValidTo))
  Where not (i.ValidTo=t.Validto and i.ValidFrom=t.ValidFrom)

ただし、これにより正確な重複範囲が可能になるため、2つの列に一意の制約を追加する必要があります。実際には、同じ日に開始(または終了)する2つの範囲はデフォルトで重複しているため、各列に一意の制約が必要になる場合があります。

于 2010-10-26T15:12:45.350 に答える