「CREATE UNIQUE INDEX」の「WHERE」句で「LEN」関数を使用します


12

私はこの表を持っています:

CREATE TABLE Table01 (column01 nvarchar(100));

そして、この条件LEN(column01)> = 5column01に一意のインデックスを作成したい

私は試した:

CREATE UNIQUE INDEX UIX_01 ON Table01(column01) WHERE LEN(column01) >= 5;

私が得た:

テーブル 'Table01'のフィルター選択されたインデックス 'UIX_01'のWHERE句が正しくありません。

そして:

ALTER TABLE Table01 ADD column01_length AS (LEN(column01));
CREATE UNIQUE INDEX UIX_01 ON Table01(column01) WHERE column01_length >= 5;

生産物:

フィルター式の列「column01_length」が計算列であるため、フィルター索引「UIX_01」を表「Table01」に作成できません。この列が含まれないようにフィルター式を書き直してください。

回答:


15

フィルター選択されたインデックスの制限を回避する1つの方法は、インデックス付きビューを使用することです。

CREATE TABLE dbo.Table01 (
  Column01 NVARCHAR(100)
);
GO

CREATE VIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING AS
SELECT Column01
FROM dbo.Table01
WHERE LEN(Column01) >= 5;
GO

CREATE UNIQUE CLUSTERED INDEX cdx
    ON dbo.vw_Table01_Column01_LenOver5Unique(Column01);
GO

INSERT INTO dbo.Table01 VALUES('1'); --success
INSERT INTO dbo.Table01 VALUES('1'); --success
INSERT INTO dbo.Table01 VALUES('55555'); --success
INSERT INTO dbo.Table01 VALUES('55555'); --duplicate key error
GO

編集:

インデックスに2つの列がある場合、どのようにビューを定義する必要がありますか?CREATE UNIQUE INDEX UIX_01 ON Table01(column01、column02)WHERE LEN(column01)> = 5

インデックス付きビューのアプローチは、他のキー列をビュー定義とインデックスに追加することにより、複合キーに拡張できます。ビュー定義にも同じフィルターが適用されますが、修飾する行の一意性は単一の列値ではなく複合キーによって強制されます。

CREATE TABLE dbo.Table01 (
   Column01 NVARCHAR(100)
  ,Column02 NVARCHAR(100)
);
GO

CREATE VIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING AS
SELECT Column01, Column02
FROM dbo.Table01
WHERE LEN(Column01) >= 5;
GO

CREATE UNIQUE CLUSTERED INDEX cdx
    ON dbo.vw_Table01_Column01_LenOver5Unique(Column01, Column02)
GO

INSERT INTO dbo.Table01 VALUES('1','A'); --success
INSERT INTO dbo.Table01 VALUES('1','A'); --success
INSERT INTO dbo.Table01 VALUES('55555','A'); --success
INSERT INTO dbo.Table01 VALUES('55555','B'); --success
INSERT INTO dbo.Table01 VALUES('55555','B'); --duplicate key error
GO

そして、私はこれが私の極悪非道よりもはるかに良いパフォーマンスを期待しています。
ジェームズアンダーソン

@Dan Guzmanは「WITH SCHEMABINDING」を使用すべきですか?
オタク

2
@Jalilはい、SCHEMABINDINGインデックス付きビューには必須です。もちろん、テーブルを変更する前にビューをドロップする必要があります。SSDTのようなツールは、その依存関係を自動的に処理します。
ダン・グスマン

インデックスに2つの列がある場合、どのようにビューを定義する必要がありますか?CREATE UNIQUE INDEX UIX_01 ON Table01(column01、column02)WHERE LEN(column01)> = 5;
オタク

@Jalil、回答に複合キーの例を追加しました。
ダン・グスマン

5

これは、フィルター選択されたインデックスの多くの制限の別のようです。をLIKE使用WHERE column01 LIKE '_____'してこれをバイパスしようとしても機能せず、同じエラーメッセージが生成されます(「不正なWHERE句...」)。

VIEW解決策に加えて、別の方法は、計算列を通常の列に変換し、CHECK常に有効なデータを持つように制約を追加することです。

CREATE TABLE Table01 (column01 nvarchar(100),
                      column01_length int,
                      CHECK ( column01_length = len(column01)
                              AND column01 IS NOT NULL 
                              AND column01_length IS NOT NULL
                           OR column01 IS NULL 
                              AND column01_length IS NULL )
                     ) ;


CREATE UNIQUE INDEX UIX_01 ON Table01 (column01) WHERE column01_length >= 5 ;

rextester.comでテスト済み

当然、それは、(挿入および更新で)読み込むcolumn01_lengthたびに正しい長さを明示的に読み込む必要があることを意味しますcolumn01。T-SQL LEN()関数の場合と同じ方法で長さを計算する必要があるため、これは難しい場合があります。特に、末尾のスペースは無視する必要があります。これは、クライアントアプリケーションが記述されているさまざまなプログラミング言語でデフォルトで長さが計算される方法とは限りません。そもそも違いを認識しています。

オプションは、列に正しい値を提供するINSERT/UPDATEトリガー1であるため、クライアントアプリケーションに対して計算されたように表示されます。


1 制約と比較しトリガーで説明されているように、これにはINSTEAD OFトリガーを使用する必要があります。AFTERトリガーは単純に実行されません。これは、長さがないとチェック制約に違反し、トリガーの実行が妨げられるためです。ただし、INSTEAD OFトリガーには独自の制限があります(簡単な概要については、DMLトリガー計画ガイドラインを参照してください)。


1

これがどのように実行されるかはわかりませんが、これを達成するために見落としたはるかに簡単な方法があるかもしれませんが、一意性を強制することにのみ興味がある場合は必要なことを行う必要があります。

CREATE TABLE dbo.Table01 
(
  Column01 NVARCHAR(100)
);
GO

CREATE FUNCTION dbo.ChkUniqueColumn01OverLen5()
RETURNS BIT
AS
BEGIN
DECLARE @Result BIT, @Count BIGINT, @DistinctCount BIGINT

SELECT  @Count = COUNT(Column01),
        @DistinctCount = COUNT(DISTINCT Column01)
FROM    Table01
WHERE   LEN(Column01) >= 5 

SELECT @Result = CASE WHEN @Count = @DistinctCount THEN 1 ELSE 0 END

RETURN @Result

END;
GO

ALTER TABLE dbo.Table01
ADD CONSTRAINT Chk_UniqueColumn01OverLen5
CHECK (dbo.ChkUniqueColumn01OverLen5() = 1);
GO

INSERT dbo.Table01 (Column01)
VALUES (N'123'), (N'1234');
GO

INSERT dbo.Table01 (Column01)
VALUES (N'12345');
GO

INSERT dbo.Table01 (Column01)
VALUES (N'12345'); -- Will fail
GO

INSERT dbo.Table01 (Column01)
VALUES (N'123'); -- Will pass
GO

UPDATE dbo.Table01
SET Column01 = '12345'
WHERE Column01 = '1234' -- Will fail
GO

SELECT * FROM dbo.Table01;
GO

DROP TABLE Table01;
DROP FUNCTION dbo.ChkUniqueColumn01OverLen5;

2
チェック制約または計算列の定義でスカラー値関数を使用すると、列を参照していない場合でも、テーブルに接触するすべてのクエリが強制的にシリアルに実行されます。
エリックダーリン

2
@sp_BlitzErikうん、それはこの解決策について最悪のことでもないかもしれません:)。それが機能するかどうかを確認したかったため、パフォーマンスの警告が表示されました。
ジェームズアンダーソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.