기존 위반을 무시하는 고유 제한 조건을 추가 할 수 있습니까?


39

현재 열에 중복 값이있는 테이블이 있습니다.

이 잘못된 중복을 제거 할 수 없지만 고유하지 않은 추가 값이 추가되지 않도록하고 싶습니다.

UNIQUE기존 규정 준수를 확인하지 않는을 만들 수 있습니까 ?

사용을 시도 NOCHECK했지만 실패했습니다.

이 경우 라이센스 정보를 "CompanyName"에 연결하는 테이블이 있습니다.

편집 : 동일한 "CompanyName"을 가진 여러 행을 갖는 것은 잘못된 데이터이지만 현재 중복 항목을 제거하거나 업데이트 할 수 없습니다. 한 가지 접근 방식은 INSERT중복에 실패하는 저장 프로 시저를 사용하는 것입니다. SQL이 고유성을 고유하게 검사하도록 할 수 있다면 바람직합니다.

이 데이터는 회사 이름으로 쿼리됩니다. 몇 개의 기존 중복의 경우 여러 행이 리턴되어 표시됨을 의미합니다. 이것이 잘못되었지만 사용 사례에서 허용됩니다. 목표는 미래에이를 방지하는 것입니다. 저장 프로 시저 에서이 논리를 수행해야한다는 의견에서 나에게 보입니다.


테이블을 변경할 수 있습니까 (열을 하나 더 추가하십시오)?
ypercubeᵀᴹ

@ypercube 불행히도 아닙니다.
Matthew

답변:


33

대답은 "예"입니다. 필터링 된 인덱스를 사용하여이 작업을 수행 할 수 있습니다 ( 설명서 는 여기 참조 ).

예를 들어 다음을 수행 할 수 있습니다.

create unique index t_col on t(col) where id > 1000;

이전 행이 아닌 행 에서만 고유 인덱스를 작성합니다 . 이 특정 공식은 기존 값과 중복을 허용합니다.

소수의 복제본이 있다면 다음과 같이 할 수 있습니다.

create unique index t_col on t(col) where id not in (<list of ids for duplicate values here>);

2
그것이 좋은지 아닌지는 기존 아이템이 "오래된"것이 같은 가치를 가진 새로운 아이템을 생성하지 못하게하는지에 달려 있습니다.
supercat

1
@ 슈퍼 캣. . . 기존 중복 값을 제외한 모든 항목에 대해 색인을 작성하기위한 대체 공식을 제공했습니다.
Gordon Linoff 2018 년

1
후자가 작동하려면 중복 된 각 개별 키 값에 대해 하나의 id를 목록에서 생략했는지 확인해야하며 목록에서 의도적으로 생략 된 항목이 테이블에서 제거되었는지 확인해야합니다. 키가 같은 항목은 목록에서 제거됩니다.
supercat

@ 슈퍼 캣. . . 동의한다. 트리거에서 인덱스를 다시 만들 수 없기 때문에 업데이트 및 삭제에 대해 인덱스 일관성을 유지하는 것은 더욱 어려워집니다. 어쨌든, OP 또는 데이터, 또는 적어도 복제본이 자주 변경되지 않는다는 인상을 받았습니다.
Gordon Linoff 2018 년

ID 목록 대신 값 목록을 제외하지 않는 이유는 무엇입니까? 그런 다음 제외 된 ID 목록에서 중복 된 값당 하나의 ID를 제외하지 않아도됩니다.
JMD Coalesce

23

네 그렇습니다.

중복 된 테이블은 다음과 같습니다.

CREATE TABLE dbo.Party
  (
    ID INT NOT NULL
           IDENTITY ,
    CONSTRAINT PK_Party PRIMARY KEY ( ID ) ,
    Name VARCHAR(30) NOT NULL
  ) ;
GO

INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Frodo Baggins' ),
        ( 'Luke Skywalker' ),
        ( 'Luke Skywalker' ),
        ( 'Harry Potter' ) ;
GO

기존 복제본을 무시하고 새 복제본을 추가 할 수 없도록하십시오.

-- Add a new column to mark grandfathered duplicates.
ALTER TABLE dbo.Party ADD IgnoreThisDuplicate INT NULL ;
GO

-- The *first* instance will be left NULL.
-- *Secondary* instances will be set to their ID (a unique value).
UPDATE  dbo.Party
SET     IgnoreThisDuplicate = ID
FROM    dbo.Party AS my
WHERE   EXISTS ( SELECT *
                 FROM   dbo.Party AS other
                 WHERE  other.Name = my.Name
                        AND other.ID < my.ID ) ;
GO

-- This constraint is not strictly necessary.
-- It prevents granting further exemptions beyond the ones we made above.
ALTER TABLE dbo.Party WITH NOCHECK
ADD CONSTRAINT CHK_Party_NoNewExemptions 
CHECK(IgnoreThisDuplicate IS NULL);
GO

SELECT * FROM dbo.Party;
GO

-- **THIS** is our pseudo-unique constraint.
-- It works because the grandfathered duplicates have a unique value (== their ID).
-- Non-grandfathered records just have NULL, which is not unique.
CREATE UNIQUE INDEX UNQ_Party_UniqueNewNames ON dbo.Party(Name, IgnoreThisDuplicate);
GO

이 솔루션을 테스트 해 보겠습니다.

-- cannot add a name that exists
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Frodo Baggins' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.

-- cannot add a name that exists and has an ignored duplicate
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Luke Skywalker' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.


-- can add a new name 
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Hamlet' );

-- but only once
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Hamlet' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.

4
그는 테이블에 열을 추가 할 수 없습니다.
Aaron Bertrand

3
이 답변이 고유 한 제약 조건에서 NULL 값을 비표준 방식으로 유용한 것으로 변환하는 방법을 좋아합니다. 교활한 속임수.
ypercubeᵀᴹ

@ ypercubeᵀᴹ, 고유 제약 조건에서 NULL 처리에 대해 비표준이 무엇인지 설명 할 수 있습니까? 예상했던 것과 어떻게 다릅니 까? 감사!
Noach

1
SQL Server의 @Noach에서 UNIQUENull을 허용하는 열의 제약 조건은 최대 하나의 NULL값만 보장합니다 . SQL 표준 (및 거의 모든 다른 SQL DBMS)은 많은 수의 NULL값을 허용해야한다고 규정 합니다 (즉, 제약 조건은 null 값을 무시해야 함).
ypercubeᵀᴹ

@ ypercubeᵀᴹ 따라서 다른 DBMS에서 이것을 구현하려면 NULL 대신 DEFAULT 0을 사용해야합니다. 옳은?
Noach

16

필터링 된 고유 인덱스는 훌륭한 아이디어이지만 WHERE identity_column > <current value>조건 을 사용하든 관계없이 사소한 단점이 있습니다 WHERE identity_column NOT IN (<list of ids for duplicate values here>).

첫 번째 방법을 사용하면 향후에도 기존 (현재) 데이터의 복제본으로 중복 데이터를 삽입 할 수 있습니다. 예를 들어, 이제로 행이 하나만 CompanyName = 'Software Inc.'있는 경우 인덱스는 회사 이름이 같은 행을 하나 더 삽입하는 것을 금지하지 않습니다. 두 번 시도하면 금지됩니다.

두 번째 접근 방식을 사용하면 개선 사항이 있으므로 위의 방법은 효과가 없습니다 (좋은). 그러나 여전히 더 많은 복제본 또는 기존 복제본을 삽입 할 수 있습니다. 예를 들어,로 행이 두 개 이상인 CompanyName = 'DoubleData Co.'경우 인덱스는 회사 이름이 같은 행을 하나 이상 삽입하는 것을 금지하지 않습니다. 두 번 시도하면 금지됩니다.

(업데이트) 모든 중복 이름에 대해 제외 목록에서 하나의 ID를 유지하는 경우 수정할 수 있습니다. 위의 예와 같이 duplicate CompanyName = DoubleData Co.와 IDs를 가진 4 개의 행이있는 4,6,8,9경우 제외 목록에는 이러한 ID 중 3 개만 있어야합니다.

두 번째 방법을 사용하면 SQL-Server 가 필터링 된 인덱스 NOT INWHERE일부 에서 연산자를 지원하지 않는 것처럼 보이기 때문에 번거로운 조건이 있습니다 (처음에 얼마나 많은 복제본이 있는지에 따라 얼마나 번거로운 지에 따라 다름) . SQL-Fiddle을 참조하십시오 . 대신 수백 개의 중복 이름이있는 경우 그러한 조건에 효율성에 영향을 미치는지 확실하지 않은 WHERE (CompanyID NOT IN (3,7,4,6,8,9))것과 같은 WHERE (CompanyID <> 3 AND CompanyID <> 7 AND CompanyID <> 4 AND CompanyID <> 6 AND CompanyID <> 8 AND CompanyID <> 9)것을 가져야합니다.


@Alex Kuznetsov와 유사한 또 다른 솔루션은 다른 열을 추가하고 순위 번호로 채우고이 열을 포함하여 고유 색인을 추가하는 것입니다.

ALTER TABLE Company
  ADD Rn TINYINT DEFAULT 1;

UPDATE x
SET Rn = Rnk
FROM
  ( SELECT 
      CompanyID,
      Rn,
      Rnk = ROW_NUMBER() OVER (PARTITION BY CompanyName 
                               ORDER BY CompanyID)
    FROM Company 
  ) x ;

CREATE UNIQUE INDEX CompanyName_UQ 
  ON Company (CompanyName, Rn) ; 

그런 다음 DEFAULT 1속성과 고유 인덱스로 인해 이름이 중복 된 행을 삽입 할 수 없습니다 . 이것은 여전히 ​​100 % 바보가 아닙니다 (알렉스가있는 동안). 명령문에 Rn명시 적으로 설정되어 INSERT있거나 Rn값이 악의적으로 업데이트 된 경우에도 중복이 계속 발생합니다 .

SQL- 피들 -2


-2

또 다른 대안은 테이블에 값이 이미 있는지 확인하는 스칼라 함수를 작성한 다음 검사 제한 조건에서 해당 함수를 호출하는 것입니다.

이것은 성능에 끔찍한 일을 할 것입니다.



Aaron이 지적한 문제 외에도 대답은이 검사 제한 조건을 추가하여 기존 중복을 무시하는 방법을 설명하지 않습니다.
ypercubeᵀᴹ

-2

동일한 것을 찾고 있습니다-신뢰할 수없는 고유 색인을 작성하여 기존 불량 데이터를 무시하지만 새 레코드는 이미 존재하는 것과 중복 될 수 없습니다.

이 스레드를 읽는 동안 더 나은 해결책은 부모 테이블에 대해 [삽입]을 검사하여 해당 테이블 사이에 중복이 있는지 확인하는 트리거를 작성하는 것입니다. ROLLBACK TRAN.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.