“CREATE UNIQUE INDEX”의“WHERE”절에서“LEN”기능 사용


12

나는이 테이블을 가지고있다 :

CREATE TABLE Table01 (column01 nvarchar(100));

그리고 나는 고유 인덱스 생성 할 column01 이 조건 LEN (column01)> = 5

나는 시도했다 :

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'열이 계산 열이므로 'Table01'테이블에서 필터링 된 인덱스 'UIX_01'을 (를) 작성할 수 없습니다. 이 열을 포함하지 않도록 필터 표현식을 다시 작성하십시오.

답변:


15

필터링 된 인덱스 제한을 해결하는 한 가지 방법은 인덱스 된 뷰를 사용하는 것입니다.

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

편집하다:

인덱스에 두 개의 열이있는 경우 뷰를 어떻게 정의해야합니까? Table01 (column01, column02)에서 UNIQUE INDEX UIX_01 만들기 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은 'SCHEMABINDING'을 사용해야합니까?
괴짜

2
@Jalil 예, SCHEMABINDING인덱스 된 뷰에 필요합니다. 물론 테이블을 변경하기 전에 뷰를 삭제해야합니다. SSDT와 같은 툴링은 이러한 종속성을 자동으로 처리합니다.
Dan Guzman

인덱스에 두 개의 열이있는 경우 뷰를 어떻게 정의해야합니까? Table01 (column01, column02)에서 UNIQUE INDEX UIX_01 만들기 LEN (column01)> = 5;
괴짜

@ Jalil, 나는 복합 키 예제를 내 대답에 추가했습니다.
Dan Guzman

5

이것은 필터링 된 인덱스의 많은 제한 사항 중 하나 인 것 같습니다. 를 LIKE사용하여 우회하려고 WHERE column01 LIKE '_____'해도 작동하지 않아 동일한 오류 메시지가 생성됩니다 ( "Incorrect WHERE clause ..." ).

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
검사 제한 조건 또는 계산 열 정의에서 스칼라 값 함수를 사용하면 테이블을 터치하는 모든 쿼리가 열을 참조하지 않더라도 직렬로 실행됩니다.
Erik Darling

2
@sp_BlitzErik Yep 그리고 그것은이 솔루션에 대한 최악의 상황이 아닐 수도 있습니다 :). 나는 그것이 작동하는지 확인하고 싶기 때문에 성능 경고가 발생했습니다.
James Anderson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.