7

SQL Serverへのデータベースの移行を実行しており、レガシーアプリをサポートするために、レガシーアプリが期待するとおりにデータを表示するビューをSQLServerテーブルに定義しました。

ただし、フィールドにデフォルト値がある場合、これらのビューで定義されたINSTEADOFINSERTトリガーで問題が発生しています。

例を挙げてみましょう。

データベース内のテーブルには、a、b、およびcの3つのフィールドがあります。cはまったく新しいものであり、レガシーアプリはそれを認識しないため、aとbの2つのフィールドを持つビューもあります。

レガシーアプリがビューに値を挿入しようとすると、INSTEAD OF INSERTトリガーを使用して、フィールドcに入力する必要のある値を次のように検索します。

INSERT INTO realTable(a, b, c) SELECT Inserted.a, Inserted.b, Calculated.C FROM...

(ルックアップの詳細は関係ありません。)

フィールドbにデフォルト値がない限り、このトリガーは適切に機能します。これは、クエリが

INSERT INTO legacyView(a) VALUES (123)

が実行された後、トリガーでInserted.bはNULLであり、bのデフォルト値ではありません。今、私は問題を抱えています。なぜなら、デフォルト値をbに入れる上記のクエリと、これとの違いがわからないからです。

INSERT INTO legacyView(a,b) VALUES (123, NULL)

bがNULL以外の場合でも、トリガーにINSERTクエリを記述して、bに値が指定されている場合はその値がトリガーで使用されるようにする方法がわかりませんが、そうでない場合はデフォルトが代わりに使用されます。

編集:トリガーのデフォルト値を複製したくないと付け加えました。デフォルト値はすでにデータベーススキーマにあります。直接使用できるといいのですが。

4

3 に答える 3

1

いくつかのアイデア:

  • レガシーアプリケーションがINSERTの列リストを指定し、SELECT *を使用するのではなく列に名前を付ける場合、デフォルトを列cにバインドして、アプリケーションに元の(変更された)テーブルを使用させることはできませんか?

  • レガシーアプリがINSERTにSELECTまたはDELETEとは異なるビューまたはテーブルを使用するようにする方法がある場合は、そのテーブルに必要なデフォルトを設定し、通常のアフタートリガーを使用して新しい列をに移動できます。実際のテーブル。

  • 元のテーブルをそのままにして、元のテーブルと1対1の関係にある別のテーブルに列を追加してはどうでしょうか。次に、これら2つのテーブルを組み合わせたビューを作成し、この新しいビューにトリガーの代わりに適切なトリガーを配置して、2つのテーブルに分割されたすべてのデータ操作を処理します。これにはパフォーマンスへの影響があることはわかっていますが、問題を回避する唯一の方法である可能性があります。これはマテリアライズドビューの理想的なケースであり、更新は遅くなりますが、結果は読み取り用のテーブルとまったく同じように実行されます。(マテリアライズド・ビューは、内部結合に最適であり、集約を必要としません。また、ソース表にスキーマ・ロックを設定します。)

  • ビューのUPDATEトリガーではなく、意図的にNULL値とスキップされた列の違いがわからないという同様の問題が発生しました。最終的に、ビューでINSERTトリガーを作成して、挿入を更新に変換しました(キーが既に存在する場合は更新であり、そうでない場合は挿入でした)。これは直接あなたを助けることはありませんが、それはあなたや他の人のためにいくつかのアイデアに拍車をかけるかもしれません。

于 2010-02-04T22:19:33.803 に答える
1

ポール:私はこれを解決しました。最終的。少し汚い解決策であり、誰もが好むとは限らないかもしれませんが、私はSQLServerなどにまったく慣れていません。

代わりに_of_INSERTトリガーで:

  1. 挿入された仮想テーブルのデータ構造を一時テーブルにコピーします。

    SELECT * INTO aTempInserted FROM Inserted WHERE 1=2
    
  2. ビューを作成して、(システムテーブルから)ビューの基になるテーブルのデフォルトの制約を決定し、それらを使用して、一時テーブルの制約を複製するステートメントを作成します。

    SELECT  'ALTER TABLE dbo.aTempInserted
                   ADD CONSTRAINT ' + dc.name + 'Temp' +
                   ' DEFAULT(' + dc.definition + ') 
                   FOR ' + c.name AS Cmd, OBJECT_NAME(c.object_id) AS Name
      FROM  sys.default_constraints AS dc
     INNER  JOIN sys.columns AS c
              ON dc.parent_object_id = c.object_id 
             AND dc.parent_column_id = c.column_id
    
  3. カーソルを使用して、取得したセットを反復処理し、各ステートメントを実行します。これにより、挿入先のテーブルと同じデフォルトの一時テーブルが残ります。

  4. デフォルトのレコードを一時テーブルに挿入します(挿入された仮想テーブルから作成されたすべてのフィールドはnull可能です)。

    INSERT INTO aTempInserted DEFAULT VALUES
    
  5. 挿入された仮想テーブルからビューの基になるテーブルにレコードをコピーし(トリガーがこれを妨げなかった場合、元々挿入されていたはずです)、一時テーブルを結合してデフォルト値を提供します。これには、COALESCE関数を使用して、指定されていない値のみがデフォルトになるようにする必要があります。

    INSERT INTO realTable([a], [b], 
                SELECT COALESCE(I.[a], T.[a]),
                       COALESCE(I.[a], T.[b])
                FROM   Inserted      AS I,
                       aTempInserted AS T
    
  6. 一時テーブルを削除します

于 2010-02-08T10:39:09.923 に答える
0

このようなものを使用するのはどうですか?:

insert into realtable
values inserted.a, isnull(inserted.b, DEFAULT), computedC
from inserted
于 2011-12-15T17:02:10.467 に答える