3

基本金額に応じた価格を含むテーブルがあります。例として、基準額が 100 以下の場合、価格は 10 ですが、基準額が 100 より大きく 1000 以下の場合、価格は 20 であり、最終的に基準額が 100 より大きい場合は、 1000 の場合、価格は 30 です。このテーブルの単純化されたバージョンは次のようになります。

PRICE_CODE START_RANGE     END_RANGE  PRICE_AMOUNT 
100               0,00        100,00         10,00           
100             100,01       1000,00         20,00          
100            1000,01   99999999,99         30,00           
110               0,00   99999999,99         15,00 

列レベルのチェック制約を使用すると、各レコードに有効な範囲情報が含まれていることを簡単に確認できます。問題は、次の例のように、各価格コードの範囲情報にオーバーラップやギャップが含まれていないことを確認するために、ある種のテーブル レベルのチェック制約も必要であることです。

PRICE_CODE START_RANGE     END_RANGE  PRICE_AMOUNT 
100               0,00        200,00         10,00           
100             100,01       1000,00         20,00          
100            1100,01   99999999,99         30,00           

動作している検証手順を作成しましたが、データベース内に検証ロジックを呼び出す場所が見つからないという問題があります。当然、レコード レベルのトリガーを配置することはできませんが、ステートメント レベルのトリガーは、個別の挿入、更新、および削除を行うことができ、最終結果に対してのみ範囲を検証する必要がある場合には機能しません。検証ロジックは次のようになります。

SELECT * FROM (              
SELECT price_code, start_range, end_range, price_amount
     , lag (end_range) OVER (PARTITION BY price_code ORDER BY end_range) prev_end
     , lead (start_range) OVER (PARTITION BY price_code ORDER BY start_range) next_start
  FROM my_test
ORDER BY price_code, start_range, end_range) 
 WHERE start_range <= prev_end
    OR end_range >= next_start
    OR (next_start - end_range) > 0.01
    OR (start_range - prev_end) > 0.01

1 つの方法はもちろん、データ アクセス レイヤーに検証ロジックを配置することですが、それでも SQL を直接使用して検証を回避することができます。私が興味を持っているのは、この種の「テーブルレベルの制約」をデータベースに実装して、誰も無効な範囲データをコミットできないようにする方法について誰かがアイデアを持っているかどうかです。私たちは主に Oracle を使用しているので、Oracle ベースのソリューションに興味がありますが、他の RDBMS がこの問題をどのように解決したかにも興味があります。

4

3 に答える 3

4

end_range 列は必要ですか? end_range 値は、次に高い start_range 値になることもあります。このようにすれば、隙間や重なりはできません。

于 2012-11-20T07:40:43.310 に答える
2

これを実装できる 1 つの方法は、外部キーを相互に参照することです。

MERGEこれが機能するには、ステートメントまたは遅延制約をサポートするデータベースが必要になる傾向があり、そのUNIQUE制約では単一のNULL(またはこれに対するいくつかの回避策) のみが許可されます。

あなたがすることは、最初に半開間隔を使用して範囲を表すことに切り替えることです。これを行うと、1 つの間隔の終わりを別の行の開始点への外部キー参照にすることができます。また、その逆も可能です。

どこかで方言を使用する場合、それは Oracle ではなく TSQL である可能性が高いです。これは私が慣れ親しんでいるためですが、同じ概念が適用されるはずです。

次のようなテーブルを作成します。

CREATE TABLE T (
    PRICE_CODE int not null,
    START_RANGE decimal(10,2) null,
    END_RANGE decimal(10,2) null,
    constraint UQ_T_START UNIQUE (PRICE_CODE,START_RANGE),
    constraint UQ_T_END UNIQUE (PRICE_CODE,END_RANGE),
    constraint FK_T_PREV FOREIGN KEY (PRICE_CODE,START_RANGE) references T (PRICE_CODE,END_RANGE),
    constraint FK_T_NEXT FOREIGN KEY (PRICE_CODE,END_RANGE) references T (PRICE_CODE,START_RANGE),
    constraint CK_T_SANERANGE CHECK (START_RANGE < END_RANGE)
)

1 つの行だけに を許可することでNULL START_RANGE、最低範囲を表すことができるのは 1 つの行だけになります。同様に、 forEND_RANGEと最高範囲。間にあるすべての行は、前と次の範囲行を参照する必要があります。

MERGEたとえば、最後に新しい行を挿入するには、この行を挿入し (前の行を参照)、前の行を更新する (新しい行を参照する) 必要があるため、遅延制約またはステートメントが必要です。満たされるすべての制約。これには、 および と の間で制約チェックが発生しないかINSERTUPDATEまたはMERGE両方を 1 つのステートメントで実行できるステートメントが必要です。


下限と上限の範囲を未定義の境界のままにしたくない場合は、NULL START_RANGEまたはを含む行が有効な範囲END_RANGEを表さないというルールを課すだけです。ただし、上記の構造が機能するように、これらの行をテーブルに保持してください。

于 2012-11-20T08:32:19.813 に答える
2

高速リフレッシュされたマテリアライズド ビューを利用した、テーブル レベル (またはセット レベル) の制約適用アプローチの概念を見てきました。

アイデアは、セット レベルの要件を MV クエリ内の行レベルの要件に変換し、従来の行ベースのチェック制約をマテリアライズド ビューの行に適用することです。

たとえば、ユーザーによるエントリ数を特定の量に制限する場合は、select-count-group-by-user マットを作成します。check(mv_count_column <= desired_max)表示してから、制約を適用します。

ただし、高速リフレッシュされたマットビューには多くの制限があるため、このアプローチの実装とサポートは間違いなく困難です。高速リフレッシュされた MV では分析関数がサポートされていないため、あなたのケースでまったく機能するかどうかはわかりません。回避できる可能性があります。

于 2012-11-20T08:21:45.897 に答える