sql >> データベース >  >> RDS >> Database

NULLの複雑さ–パート4、標準の一意性制約がありません

    この記事は、NULLの複雑さに関するシリーズのパート4です。以前の記事(パート1、パート2、およびパート3)では、欠落値のマーカーとしてのNULLの意味、比較やその他のクエリ要素でのNULLの動作、およびそうでない標準のNULL処理機能について説明しました。まだT-SQLで利用できます。今月は、ISO / IEC SQL標準で一意性制約を定義する方法と、T-SQLで機能する方法の違いについて説明します。また、標準機能が必要な場合に実装できるカスタマイズされたソリューションも提供します。

    標準のUNIQUE制約

    SQL Serverは、一意の制約を適用する目的で、NULL以外の値と同じようにNULLを処理します。つまり、Tの2つの行R1とR2が存在せず、R1とR2の一意の列にNULLと非NULL値の同じ組み合わせがある場合にのみ、Tの一意の制約が満たされます。たとえば、INTデータ型のNULL可能列であるcol1に一意性制約を定義するとします。 col1に値1が含まれる複数の行が拒否されるのと同じように、col1にNULLが含まれる複数の行になるような方法でテーブルを変更しようとすると拒否されます。

    NULL可能INT列col1とcol2の組み合わせに複合一意制約を定義するとします。 (col1、col2)値の次の組み合わせのいずれかが複数回発生するような方法でテーブルを変更しようとすると、拒否されます:(NULL、NULL)、(3、NULL)、(NULL、300 )、(1、100)。

    ご覧のとおり、一意性制約のT-SQL実装は、一意性を強制する目的で、NULLを非NULL値と同じように扱います。

    テーブルYを参照するテーブルXに外部キーを定義する場合は、次のいずれかのオプションを使用して、参照される列に一意性を適用する必要があります。

    • 主キー
    • 一意の制約
    • フィルタリングされていない一意のインデックス

    NULL可能列では主キーは使用できません。一意性制約(カバーの下にインデックスを作成する)と明示的に作成された一意性インデックスの両方がNULL可能列で許可され、前述のロジックを使用してT-SQLでそれらの一意性を適用します。参照テーブルの参照列にNULLが含まれる行があるかどうかに関係なく、参照テーブルには参照列にNULLが含まれる行を含めることができます。アイデアは、オプションの関係をサポートすることです。参照テーブルの一部の行は、参照テーブルのどの行にも関連していない行である可能性があります。これは、参照列でNULLを使用して実装します。

    一意性制約のT-SQL実装を示すために、次のコードを実行します。これにより、NULL可能INT列col1で定義された一意性制約を持つT3というテーブルが作成され、いくつかのサンプル行が入力されます。

    USE tempdb;
    GO
     
    DROP TABLE IF EXISTS dbo.T3;
    GO
     
    CREATE TABLE dbo.T3(col1 INT NULL, col2 INT NULL, CONSTRAINT UNQ_T3 UNIQUE(col1));
     
    INSERT INTO dbo.T3(col1, col2) VALUES(1, 100),(2, -1),(NULL, -1),(3, 300);

    次のコードを使用してテーブルをクエリします。

    SELECT * FROM dbo.T3;

    このクエリは次の出力を生成します:

    col1        col2
    ----------- -----------
    1           100
    2           -1
    NULL        -1
    3           300

    col1にNULLの2番目の行を挿入しようとしました:

    INSERT INTO dbo.T3(col1, col2) VALUES(NULL, 400);

    この試行は拒否され、次のエラーが発生します:

    メッセージ2627、レベル14、状態1
    UNIQUEKEY制約「UNQ_T3」の違反。オブジェクト'dbo.T3'に重複するキーを挿入できません。重複するキー値は()です。

    標準の一意性制約の定義は、T-SQLバージョンとは少し異なります。主な違いは、NULL処理に関係しています。標準からの一意の制約の定義は次のとおりです。

    「Tの2つの行R1とR2が存在せず、R1とR2の一意の列に同じ非NULL値がある場合にのみ、Tの一意の制約が満たされます。」

    したがって、col1に一意の制約があるテーブルTは、col1にNULLを持つ複数の行を許可しますが、col1に同じ非NULL値を持つ複数の行を許可しません。

    説明するのが少し難しいのは、複合一意制約を使用した標準に従って何が起こるかです。 (col1、col2)に一意性制約が定義されているとします。 (NULL、NULL)を使用して複数の行を作成できますが、(1、100)を使用して複数の行を作成できないのと同様に、(3、NULL)を使用して複数の行を作成することはできません。同様に、(NULL、300)で複数の行を持つことはできません。重要なのは、一意の列に同じ非NULL値を持つ複数の行を含めることは許可されていないということです。外部キーに関しては、参照テーブルに何が存在するかに関係なく、参照テーブルに任意の数の行を含めることができ、すべての参照列にNULLを含めることができます。このような行は、参照されるテーブルのどの行にも関連していません(オプションの関係)。ただし、参照する列のいずれかにNULL以外の値がある場合は、参照される列に同じNULL以外の値を持つ行が参照されるテーブルに存在する必要があります。

    プラットフォームに標準の一意性制約をサポートするデータベースがあり、そのデータベースをSQLServerに移行する必要があるとします。一意の列がNULLをサポートしている場合、SQLServerでの一意の制約の適用で問題が発生する可能性があります。ソースシステムで有効と見なされたデータは、SQLServerでは無効と見なされる場合があります。次のセクションでは、SQLServerで考えられるいくつかの回避策について説明します。

    ソリューション1、フィルター処理されたインデックスまたはインデックス付きビューを使用

    ターゲット列が1つしかない場合に、標準の一意の制約機能を適用するためのT-SQLの一般的な回避策は、ターゲット列がNULLでない行のみをフィルター処理する一意のフィルター処理されたインデックスを使用することです。次のコードは、T3から既存の一意性制約を削除し、そのようなインデックスを実装します。

    ALTER TABLE dbo.T3 DROP CONSTRAINT UNQ_T3;
     
    CREATE UNIQUE NONCLUSTERED INDEX idx_col1_notnull ON dbo.T3(col1) WHERE col1 IS NOT NULL;

    インデックスはcol1がNULLでない行のみをフィルタリングするため、そのUNIQUEプロパティはNULL以外のcol1値にのみ適用されます。

    T3にはすでにcol1にNULLの行があることを思い出してください。このソリューションをテストするには、次のコードを使用して、col1にNULLを含む2番目の行を追加します。

    INSERT INTO dbo.T3(col1, col2) VALUES(NULL, 400);

    このコードは正常に実行されます。

    T3にはすでにcol1に値1の行があることを思い出してください。次のコードを実行して、col1に1を含む2番目の行を追加してみます。

    INSERT INTO dbo.T3(col1, col2) VALUES(1, 500);

    予想どおり、この試行は次のエラーで失敗します:

    メッセージ2601、レベル14、状態1
    一意のインデックス「idx_col1_notnull」を持つオブジェクト「dbo.T3」に重複するキー行を挿入できません。重複するキー値は(1)です。

    次のコードを使用してT3を照会します。

    SELECT * FROM dbo.T3;

    このコードは、col1にNULLがある2つの行を示す次の出力を生成します。

    col1        col2
    ----------- -----------
    1           100
    2           -1
    NULL        -1
    3           300
    NULL        400

    このソリューションは、1つの列のみに一意性を適用する必要がある場合、およびその列を指す外部キーを使用して参照整合性を適用する必要がない場合にうまく機能します。

    外部キーの問題は、SQL Serverが主キー、一意の制約、または参照される列で定義された一意のフィルターされていないインデックスを必要とすることです。参照される列に一意のフィルター処理されたインデックスのみが定義されている場合は機能しません。 T3.col1を参照する外部キーを使用してテーブルを作成してみましょう。まず、次のコードを使用してテーブルT3を作成します。

    DROP TABLE IF EXISTS dbo.T3FK;
    GO
     
    CREATE TABLE dbo.T3FK
    (
      id INT NOT NULL IDENTITY CONSTRAINT PK_T3FK PRIMARY KEY,
      col1 INT NULL, 
      col2 INT NULL, 
      othercol VARCHAR(10) NOT NULL
    );

    次に、T3FK.col1からT3.col1を指す外部キーを追加するために、次のコードを実行してみてください。

    ALTER TABLE dbo.T3FK ADD CONSTRAINT FK_T3_T3FK
      FOREIGN KEY(col1) REFERENCES dbo.T3(col1);

    この試行は次のエラーで失敗します:

    メッセージ1776、レベル16、状態0
    参照テーブル「dbo.T3」には、外部キー「FK_T3_T3FK」の参照列リストと一致する主キーまたは候補キーがありません。

    メッセージ1750、レベル16、状態1
    制約またはインデックスを作成できませんでした。以前のエラーを参照してください。

    この時点で、クリーンアップのために既存のフィルタリングされたインデックスを削除します。

    DROP INDEX idx_col1_notnull ON dbo.T3;

    テーブルT3FKは後の例で使用するため、削除しないでください。

    フィルター処理されたインデックスソリューションのもう1つの問題は、外部キーが必要ない場合、たとえば組み合わせ(col1、col2)など、複数の列に標準の一意性制約機能を適用する必要がある場合に機能しないことです。 。標準の一意性制約では、一意性列の値のNULL以外の重複の組み合わせが許可されていないことに注意してください。フィルター処理されたインデックスを使用してこのロジックを実装するには、一意の列のいずれかがNULLでない行のみをフィルター処理する必要があります。別の言い方をすれば、すべての一意の列にNULLがない行のみをフィルタリングする必要があります。残念ながら、フィルター処理されたインデックスでは、非常に単純な式しか使用できません。列のOR、NOT、または操作はサポートされていません。したがって、現在、次のインデックス定義はいずれもサポートされていません。

    CREATE UNIQUE NONCLUSTERED INDEX idx_customunique ON dbo.T3(col1, col2)
      WHERE col1 IS NOT NULL OR col2 IS NOT NULL;
     
    CREATE UNIQUE NONCLUSTERED INDEX idx_customunique ON dbo.T3(col1, col2)
      WHERE NOT (col1 IS NULL AND col2 IS NULL);
     
    CREATE UNIQUE NONCLUSTERED INDEX idx_customunique ON dbo.T3(col1, col2)
      WHERE COALESCE(col1, col2) IS NOT NULL;

    このような場合の回避策は、T3からcol1とcol2を返すクエリに基づいて、上記のWHERE句のいずれかを使用し、(col1、col2)に一意のクラスター化インデックスを付けてインデックス付きビューを作成することです。

    CREATE VIEW dbo.T3CustomUnique WITH SCHEMABINDING
    AS
      SELECT col1, col2 FROM dbo.T3 WHERE col1 IS NOT NULL OR col2 IS NOT NULL;
    GO
     
    CREATE UNIQUE CLUSTERED INDEX idx_col1_col2 ON dbo.T3CustomUnique(col1, col2);
    GO

    (col1、col2)に(NULL、NULL)を含む複数の行を追加することはできますが、(col1、col2)に(3 、NULL)または(NULL、300)または(1、100)。それでも、このソリューションは外部キーをサポートしていません。

    この時点で、クリーンアップのために次のコードを実行します。

    DROP VIEW IF EXISTS dbo.T3CustomUnique;

    ソリューション2、代理キーと計算列を使用

    外部キーをサポートする必要がない限り、フィルター処理されたインデックスとインデックス付きビューを使用したソリューションは適切です。しかし、参照整合性を強制する必要がある場合はどうなりますか? 1つのオプションは、フィルター処理されたインデックスまたはインデックス付きビューソリューションを引き続き使用して一意性を適用し、トリガーを使用して参照整合性を適用することです。ただし、このオプションは非常に高価です。

    もう1つのオプションは、外部キーをサポートする一意性部分にまったく異なるソリューションを使用することです。この解決策には、参照されるテーブル(この場合はT3)に2つの列を追加することが含まれます。 idと呼ばれる1つの列は、IDプロパティを持つ代理キーです。フラグと呼ばれる別の列は、col1がNULLの場合はidを返し、NULLでない場合は0を返す永続化された計算列です。次に、col1とflagの組み合わせに一意の制約を適用します。 2つの列と一意性制約を追加するコードは次のとおりです。

    ALTER TABLE dbo.T3
      ADD id INT NOT NULL IDENTITY,
          flag AS CASE WHEN col1 IS NULL THEN id ELSE 0 END PERSISTED,
          CONSTRAINT UNQ_T3_col1_flag UNIQUE(col1, flag);

    次のコードを使用してT3を照会します。

    SELECT * FROM dbo.T3;

    このコードは次の出力を生成します:

    col1        col2        id          flag
    ----------- ----------- ----------- -----------
    1           100         1           0
    2           -1          2           0
    NULL        -1          3           3
    3           300         4           0
    NULL        400         5           5

    参照テーブル(この場合はT3FK)については、常に0に設定されるflagという計算列と、T3の一意の列(col1、flag)を指す(col1、flag)で定義された外部キーを追加します。 :

    ALTER TABLE dbo.T3FK
      ADD flag AS 0 PERSISTED,
          CONSTRAINT FK_T3_T3FK
            FOREIGN KEY(col1, flag) REFERENCES dbo.T3(col1, flag);

    このソリューションをテストしてみましょう。

    次の行を追加してみてください:

    INSERT INTO dbo.T3FK(col1, col2, othercol) VALUES
      (1, 100, 'A'),
      (2, -1, 'B'),
      (3, 300, 'C');
    >

    すべての行に対応する参照行があるため、これらの行は正常に追加されます。

    テーブルT3FKをクエリします:

    SELECT * FROM dbo.T3FK;

    次の出力が得られます:

    id          col1        col2        othercol   flag
    ----------- ----------- ----------- ---------- -----------
    1           1           100         A          0
    2           2           -1          B          0
    3           3           300         C          0

    参照されるテーブルに対応する行がない行を追加してみてください:

    INSERT INTO dbo.T3FK(col1, col2, othercol) VALUES
      (4, 400, 'D');

    試行は拒否されますが、次のエラーが発生します。

    メッセージ547、レベル16、状態0
    INSERTステートメントがFOREIGNKEY制約「FK_T3_T3FK」と競合しました。データベース「TSQLV5」、テーブル「dbo.T3」で競合が発生しました。

    col1にNULLを含む行をT3FKに追加してみてください:

    INSERT INTO dbo.T3FK(col1, col2, othercol) VALUES
      (NULL, NULL, 'E');

    この行は、T3FK(オプションの関係)のどの行にも関連していないと見なされ、標準によれば、col1の参照テーブルにNULLが存在するかどうかに関係なく許可される必要があります。 T-SQLはこのシナリオをサポートしており、行は正常に追加されます。

    テーブルT3FKをクエリします:

    SELECT * FROM dbo.T3FK;

    このコードは次の出力を生成します:

    id          col1        col2        othercol   flag
    ----------- ----------- ----------- ---------- -----------
    1           1           100         A          0
    2           2           -1          B          0
    3           3           300         C          0
    5           NULL        NULL        E          0

    このソリューションは、単一の列に標準の一意性機能を適用する必要がある場合にうまく機能します。ただし、複数の列に一意性を適用する必要がある場合は問題があります。問題を示すために、最初にテーブルT3とT3FKを削除します。

    DROP TABLE IF EXISTS dbo.T3FK, dbo.T3;

    次のコードを使用して、(col1、col2、flag)に複合一意制約を使用してT3を再作成します。

    CREATE TABLE dbo.T3
    (
      col1 INT NULL,
      col2 INT NULL,
      id INT NOT NULL IDENTITY,
      flag AS CASE WHEN col1 IS NULL AND col2 IS NULL THEN id ELSE 0 END PERSISTED,
      CONSTRAINT UNQ_T3 UNIQUE(col1, col2, flag)
    );

    col1とcol2の両方がNULLの場合はフラグがidに設定され、それ以外の場合は0に設定されることに注意してください。

    一意性制約自体はうまく機能します。

    次のコードを実行して、(col1、col2)に(NULL、NULL)が複数出現するなど、T3にいくつかの行を追加します。

    INSERT INTO dbo.T3(col1, col2) VALUES(1, 100),(1, 200),(NULL, NULL),(NULL, NULL);

    これらの行は正常に追加されます。

    (col1、col2)に(1、NULL)の2つのオカレンスを追加してみてください:

    INSERT INTO dbo.T3(col1, col2) VALUES(1, NULL),(1, NULL);

    この試行は、次のエラーで失敗します。

    メッセージ2627、レベル14、状態1
    UNIQUEKEY制約「UNQ_T3」の違反。オブジェクト'dbo.T3'に重複するキーを挿入できません。重複するキー値は(1、、0)です。

    (col1、col2)に(NULL、100)の2つのオカレンスを追加してみてください:

    INSERT INTO dbo.T3(col1, col2) VALUES(NULL, 100),(NULL, 100);

    この試みも失敗し、次のエラーが発生します。

    メッセージ2627、レベル14、状態1
    UNIQUEKEY制約「UNQ_T3」の違反。オブジェクト'dbo.T3'に重複するキーを挿入できません。重複するキー値は(、100、0)です。

    違反が発生しない次の2行を追加してみてください。

    INSERT INTO dbo.T3(col1, col2) VALUES(3, NULL),(NULL, 300);

    これらの行は正常に追加されました。

    この時点でテーブルT3をクエリします:

    SELECT * FROM dbo.T3;

    次の出力が得られます:

    col1        col2        id          flag
    ----------- ----------- ----------- -----------
    1           100         1           0
    1           200         2           0
    NULL        NULL        3           3
    NULL        NULL        4           4
    3           NULL        9           0
    NULL        300         10          0

    これまでのところ良いです。

    次に、次のコードを実行して、T3の一意の列を参照する複合外部キーを持つテーブルT3FKを作成します。

    CREATE TABLE dbo.T3FK
    (
      id INT NOT NULL IDENTITY CONSTRAINT PK_T3FK PRIMARY KEY,
      col1 INT NULL, 
      col2 INT NULL, 
      othercol VARCHAR(10) NOT NULL,
      flag AS 0 PERSISTED,
      CONSTRAINT FK_T3_T3FK
        FOREIGN KEY(col1, col2, flag) REFERENCES dbo.T3(col1, col2, flag)
    );

    このソリューションでは、当然、(col1、col2)に(NULL、NULL)を含む行をT3FKに追加できます。問題は、他の列がNULLでなく、参照されるテーブルT3にそのようなキーの組み合わせがない場合でも、col1またはcol2のいずれかに行をNULLで追加できることです。たとえば、次の行をT3FKに追加してみてください。

    INSERT INTO dbo.T3FK(col1, col2, othercol) VALUES(5, NULL, 'A');

    T3に関連する行がない場合でも、この行は正常に追加されます。標準によれば、この行は許可されるべきではありません。

    製図板に戻る…

    ソリューション3、代理キーと計算列を使用

    前のソリューション(ソリューション2)の問題は、複合外部キーをサポートする必要がある場合に発生します。これにより、他の参照列にNULL以外の値があり、参照テーブルに関連する行がない場合でも、リスト1の参照列にNULLがある参照テーブルの行が許可されます。これに対処するために、以前のソリューションのバリエーションを使用できます。これをソリューション3と呼びます。

    まず、次のコードを使用して既存のテーブルを削除します。

    DROP TABLE IF EXISTS dbo.T3FK, dbo.T3;

    参照されるテーブル(この場合はT3)の新しいソリューションでは、IDベースのIDサロゲートキー列を引き続き使用します。また、unqpathと呼ばれる永続化された計算列を使用します。すべての一意の列(この例ではcol1とcol2)がNULLの場合、unqpathをidの文字列表現に設定します(区切り文字なし )。一意の列のいずれかがNULLでない場合は、CONCAT関数を使用して、unqpathを一意の列の値の分離されたリストの文字列表現に設定します。この関数は、NULLを空の文字列に置き換えます。重要なのは、通常はデータ自体に表示されないセパレータを使用することです。たとえば、整数のcol1とcol2の値では、数字しかありません。したがって、数字以外の区切り文字は機能します。私の例では、ドット(。)を使用します。次に、unqpathに一意性制約を適用します。すべての一意の列がNULL(idに設定)の場合と、一意の列のいずれかがNULLでない場合は、unqpathの値が競合することはありません。前者の場合、unqpathには区切り文字が含まれておらず、後者の場合は区切り文字が含まれているためです。 。複合キーケースがある場合はソリューション3を使用し、単一列のキーケースがある場合はより単純なソリューション2を使用する可能性が高いことを忘れないでください。 Solution2ではなく単一列のキーでもSolution3を使用する場合は、値が1つしかない場合でも、一意の列がNULLでないときに必ず区切り文字を追加してください。このように、col1がNULLである行のidが別の行のcol1と等しい場合、前者には区切り文字がなく、後者には区切り文字がないため、競合は発生しません。

    前述の追加機能を使用してT3を作成するためのコードは次のとおりです。

    CREATE TABLE dbo.T3
    (
      col1 INT NULL,
      col2 INT NULL,
      id INT NOT NULL IDENTITY,
      unqpath AS CASE WHEN col1 IS NULL AND col2 IS NULL THEN CAST(id AS VARCHAR(10)) 
                      ELSE CONCAT(CAST(col1 AS VARCHAR(11)), '.', CAST(col2 AS VARCHAR(11)))
                 END PERSISTED,
      CONSTRAINT UNQ_T3 UNIQUE(unqpath)
    );

    外部キーと参照テーブルを処理する前に、一意性制約をテストしましょう。一意の列での非NULL値の重複する組み合わせは許可されないはずですが、一意の列でのすべてのNULLの複数回の出現を許可することになっていることを忘れないでください。

    次のコードを実行して、(col1、col2)に(NULL、NULL)が2回出現するなど、いくつかの行を追加します。

    INSERT INTO dbo.T3(col1, col2) VALUES(1, 100),(1, 200),(NULL, NULL),(NULL, NULL);

    このコードは正常に完了します。

    (col1、col2)に(1、NULL)の2つのオカレンスを追加してみてください:

    INSERT INTO dbo.T3(col1, col2) VALUES(1, NULL),(1, NULL);

    このコードは、次のエラーで失敗します。

    メッセージ2627、レベル14、状態1
    UNIQUEKEY制約「UNQ_T3」の違反。オブジェクト'dbo.T3'に重複するキーを挿入できません。重複するキー値は(1.)です。

    同様に、次の試行も拒否されます:

    INSERT INTO dbo.T3(col1, col2) VALUES(NULL, 100),(NULL, 100);

    次のエラーが発生します:

    メッセージ2627、レベル14、状態1
    UNIQUEKEY制約「UNQ_T3」の違反。オブジェクト'dbo.T3'に重複するキーを挿入できません。重複するキー値は(.100)です。

    次のコードを実行して、さらに2行追加します。

    INSERT INTO dbo.T3(col1, col2) VALUES(3, NULL),(NULL, 300);

    このコードは正常に実行されます。

    この時点で、クエリT3:

    SELECT * FROM dbo.T3;

    次の出力が得られます:

    col1        col2        id          unqpath
    ----------- ----------- ----------- -----------------------
    1           100         1           1.100
    1           200         2           1.200
    NULL        NULL        3           3
    NULL        NULL        4           4
    3           NULL        9           3.
    NULL        300         10          .300

    unqpath値を観察し、それらの構成の背後にあるロジックと、すべての一意の列がNULL(セパレーターなし)の場合と少なくとも1つがNULLでない場合(セパレーターが存在する場合)の違いを理解していることを確認してください。

    参照テーブルについては、T3FK; unqpathという計算列も定義しますが、参照するすべての列がNULLの場合は、列をidではなくNULLに設定します。参照する列のいずれかがNULLでない場合は、T3で行ったのと同じ分離された値のリストを作成します。次に、次のように、T3.unqpathを指すT3FK.unqpathに外部キーを定義します。

    CREATE TABLE dbo.T3FK
    (
      id INT NOT NULL IDENTITY CONSTRAINT PK_T3FK PRIMARY KEY,
      col1 INT NULL, 
      col2 INT NULL, 
      othercol VARCHAR(10) NOT NULL,
      unqpath AS CASE WHEN col1 IS NULL AND col2 IS NULL THEN NULL
                      ELSE CONCAT(CAST(col1 AS VARCHAR(11)), '.', CAST(col2 AS VARCHAR(11)))
                 END PERSISTED,
      CONSTRAINT FK_T3_T3FK
        FOREIGN KEY(unqpath) REFERENCES dbo.T3(unqpath)
    );

    次の試みが示すように、この外部キーは、参照する列のいずれかがNULLでなく、参照されるテーブルT3に関連する行がないT3FKの行を拒否します。

    INSERT INTO dbo.T3FK(col1, col2, othercol) VALUES(5, NULL, 'A');

    このコードは次のエラーを生成します:

    メッセージ547、レベル16、状態0
    INSERTステートメントがFOREIGNKEY制約「FK_T3_T3FK」と競合しました。データベース「TSQLV5」、テーブル「dbo.T3」、列「unqpath」で競合が発生しました。

    このソリューションは、T3の関連する行が存在する限り、参照する列のいずれかがNULLではないT3FKの行、およびすべての参照する列にNULLがある行を示します。これは、そのような行がT3のどの行とも無関係であると見なされるためです。次のコードは、そのような有効な行をT3FKに追加します。

    INSERT INTO dbo.T3FK(col1, col2, othercol) VALUES
      (1   , 100 , 'A'),
      (1   , 200 , 'B'),
      (3   , NULL, 'C'),
      (NULL, 300 , 'D'),
      (NULL, NULL, 'E'),
      (NULL, NULL, 'F');

    このコードは正常に完了します。

    次のコードを実行して、T3FKにクエリを実行します。

    SELECT * FROM dbo.T3FK;

    次の出力が得られます:

    id          col1        col2        othercol   unqpath
    ----------- ----------- ----------- ---------- -----------------------
    2           1           100         A          1.100
    3           1           200         B          1.200
    4           3           NULL        C          3.
    5           NULL        300         D          .300
    6           NULL        NULL        E          NULL
    7           NULL        NULL        F          NULL

    そのため、少し創造性が必要でしたが、外部キーのサポートなど、標準の一意性制約の回避策があります。

    結論

    一意性制約は単純な機能だと思うかもしれませんが、一意性列でNULLをサポートする必要がある場合は、少し注意が必要になる可能性があります。 T-SQLで標準の一意性制約機能を実装する必要がある場合、2つはNULLの処理方法に関して異なるルールを使用するため、より複雑になります。この記事では、2つの違いを説明し、T-SQLで機能する回避策を提供しました。 1つのNULL可能列のみに一意性を適用する必要があり、その列を参照する外部キーをサポートする必要がない場合は、単純なフィルター処理されたインデックスを使用できます。ただし、外部キーまたは標準機能を使用した複合一意制約をサポートする必要がある場合は、代理キーと計算列を使用したより複雑な実装が必要になります。


    1. PostgreSQL13でのUnicode正規化

    2. 私の場合、OracleでSQLを作成するにはどうすればよいですか?

    3. 素晴らしい24人のコンカレントマネージャーの面接の質問

    4. Oracleの日付から短い日の名前を返す