3

I need to have a table in T-SQL which will have the following structure

KEY     Various_Columns       Flag
 1          row 1              F
 2          row_2              F
 3          row_3              T
 4          row_4              F

Either no rows, or at most one row can have the Flag column with the value T. My developer claims that this can be achieved with a check constraint placed on the table.

Questions:

  1. Can such a constraint be placed on the database itself (ie an inter-row constraint) at the database level, rather than in business rules for updating or inserting rows
  2. Is such a table in normal form?
  3. Or would normal form require removing the Flag column, and instead (say) had another simple table or variable containing the value of row which had Flag=T, ie in the above case row=3.
4

2 に答える 2

7

1 いいえ。チェック制約は行単位です。他の制約もこれを行いません。

次のいずれかが必要です。

  • トリガー (すべてのバージョン)
  • フィルター Flag = T のインデックス付きビュー、および Flag の一意のインデックス (SQL Server 2000+)
  • フィルター処理されたインデックス (SQL Server 2008)

2 十分

3 本当にやり過ぎ。上記の解決策を回避するために、同じデータを分割しています。ただし、1 行のテーブル、ID 列の FK、およびフラグに対する一意の制約を使用します。

于 2010-10-06T19:42:35.977 に答える
0

私の開発者は、テーブルに配置されたチェック制約でこれを達成できると主張しています。

SQL Server は、制約内のサブクエリを直接サポートしません** CHECK(完全な SQL-92 の要件です。SQL Server は、大まかに言えば、エントリー レベルの SQL-92 のみに準拠しています)。

SQL Server でこの制約を強制するより良い方法がほぼ確実に存在しますが、純粋に興味深いことに、実際には行レベルのCHECK制約と制約を使用して実現できUNIQUEます。たとえば、次の 1 つの方法があります。

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 Flag_key INTEGER UNIQUE, 
 CHECK (
        (Flag = 'F' AND Flag_key = key_col)
        OR
        (Flag = 'T' AND Flag_key = NULL)
       )
);

ここでの問題は、Flag_key列の値を「手動で」維持する必要があることです。列 +CHECKを計算列に置き換えると、値が自動的に維持されることを意味します。

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 Flag_key AS (
              CASE WHEN Flag = 'F' THEN key_col
                   ELSE NULL END
             ), 
 UNIQUE (Flag_key)
);

** SQL Server は制約内のサブクエリを直接サポートしていませんが、場合によってはユーザー定義関数 (UDF) を使用する回避策があります。CHECK

CREATE FUNCTION dbo.CountTFlags ()
RETURNS INTEGER
AS
BEGIN
DECLARE @return INTEGER;
SET @return = (
               SELECT COUNT(*)
                 FROM YourStuff
                WHERE Flag = 'T'
              );
RETURN @return;
END;

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 CHECK (1 >= dbo.CountTFlags())
);

UDF アプローチがすべての場合に機能するとは限らず、注意が必要であることに注意してください。重要な点は、影響を受ける行ごとに UDF が評価されることです (ご想像のとおり、SQL ステートメントまたはトランザクション レベルではなく)。この場合、影響を受けるすべての行に対して制約が真である必要があるため、--と思います! - 安全。詳細については、David Portas による Trouble with CHECK Constraints を参照してください。


個人的には、モデル化するために2番目のテーブルを使用するFlagだけで、キーと外部キーのみが含まれます。

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL
);

CREATE TABLE YourStuffFlag
(
 key_col INTEGER NOT NULL UNIQUE
    REFERENCES YourStuff (key_col)
);

[my] テーブルは通常の形式ですか?

目指すべきはフィフスノーマルフォーム(5NF)。これを達成できるかどうかは、 の設計に依存しますVarious_Columns。あなたFlagが 5NF の要件を満たしているとは思えず、更新、削除、または挿入の異常も見られません (これは正規化のポイントですが、5NF 設計は依然として異常を示す可能性があります)。とはいえ、属性を取得する行を切り替えるにはflag、私の 2 テーブルの設計では 1 つのUPDATEステートメントが必要ですが、1 つのテーブルの設計では 2 つのステートメントが必要です ;)

于 2010-10-07T09:11:33.520 に答える