[sql-server] null도 허용하는 고유 제약 조건을 어떻게 만듭니 까?

GUID로 채울 열에 고유 한 제약 조건을 갖고 싶습니다. 그러나 내 데이터에는이 열에 대한 null 값이 포함되어 있습니다. 여러 null 값을 허용하는 제약 조건을 어떻게 만듭니 까?

시나리오 예 는 다음과 같습니다 . 이 스키마를 고려하십시오.

CREATE TABLE People (
  Id INT CONSTRAINT PK_MyTable PRIMARY KEY IDENTITY,
  Name NVARCHAR(250) NOT NULL,
  LibraryCardId UNIQUEIDENTIFIER NULL,
  CONSTRAINT UQ_People_LibraryCardId UNIQUE (LibraryCardId)
)

그런 다음 달성하려는 내용은이 코드를 참조하십시오.

-- This works fine:
INSERT INTO People (Name, LibraryCardId)
 VALUES ('John Doe', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');

-- This also works fine, obviously:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Marie Doe', 'BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB');

-- This would *correctly* fail:
--INSERT INTO People (Name, LibraryCardId) 
--VALUES ('John Doe the Second', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');

-- This works fine this one first time:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Richard Roe', NULL);

-- THE PROBLEM: This fails even though I'd like to be able to do this:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Marcus Roe', NULL);

마지막 설명은 메시지와 함께 실패합니다.

UNIQUE KEY 제약 조건 ‘UQ_People_LibraryCardId’위반 ‘dbo.People’개체에 중복 키를 삽입 할 수 없습니다.

NULL실제 데이터에서 고유성을 확인하면서 여러 값을 허용하도록 스키마 및 / 또는 고유성 제약 조건을 어떻게 변경 합니까?



답변

SQL Server 2008 +

WHERE절을 사용하여 여러 NULL을 허용하는 고유 인덱스를 만들 수 있습니다 . 아래 답변을 참조하십시오 .

SQL Server 2008 이전

UNIQUE 제약 조건을 만들고 NULL을 허용 할 수 없습니다. 기본값 NEWID ()를 설정해야합니다.

UNIQUE 제약 조건을 만들기 전에 기존 값을 NEWID ()로 업데이트합니다. 여기서 NULL은 없습니다.


답변

당신이 찾고있는 것은 실제로 ANSI 표준 SQL : 92, SQL : 1999 및 SQL : 2003의 일부입니다. 즉 UNIQUE 제약 조건은 NULL이 아닌 중복 값을 허용하지 않지만 여러 NULL 값을 허용해야합니다.

그러나 SQL Server의 Microsoft 세계에서는 단일 NULL이 허용되지만 여러 NULL은 허용되지 않습니다 …

에서 SQL 서버 2008 , 당신은 제외 NULL을 그 조건 자에 따라 고유 필터링 된 인덱스를 정의 할 수 있습니다 :

CREATE UNIQUE NONCLUSTERED INDEX idx_yourcolumn_notnull
ON YourTable(yourcolumn)
WHERE yourcolumn IS NOT NULL;

이전 버전에서는 NOT NULL 술어를 사용하여 VIEWS를 사용하여 제한 조건을 적용 할 수 있습니다.


답변

SQL Server 2008 이상

고유 색인을 필터링하십시오.

CREATE UNIQUE NONCLUSTERED INDEX UQ_Party_SamAccountName
ON dbo.Party(SamAccountName)
WHERE SamAccountName IS NOT NULL;

낮은 버전에서는 구체화 된 뷰가 여전히 필요하지 않습니다.

SQL Server 2005 및 이전 버전의 경우보기없이 수행 할 수 있습니다. 방금 내 테이블 중 하나에 요청하는 것처럼 독특한 제약 조건을 추가했습니다. column SamAccountName에서 고유성을 원 하지만 여러 NULL을 허용하려면 materialized view 대신 materialized column을 사용했습니다.

ALTER TABLE dbo.Party ADD SamAccountNameUnique
   AS (Coalesce(SamAccountName, Convert(varchar(11), PartyID)))
ALTER TABLE dbo.Party ADD CONSTRAINT UQ_Party_SamAccountName
   UNIQUE (SamAccountNameUnique)

실제 원하는 고유 열이 NULL 일 때 전체 테이블에서 고유하게 보장되는 계산 열에 무언가를 넣어야합니다. 이 경우 PartyIDID 열이며 숫자가되는 것은 결코 일치하지 SamAccountName않으므로 나를 위해 일했습니다. 고유 한 방법을 시도해 볼 수 있습니다. 실제 데이터와 교차 할 가능성이 없도록 데이터의 도메인을 이해해야합니다. 다음과 같이 차별화 요소를 추가하는 것만 큼 간단 할 수 있습니다.

Coalesce('n' + SamAccountName, 'p' + Convert(varchar(11), PartyID))

해도 PartyID숫자가 아닌 언젠가되었다과 일치 수 SamAccountName, 지금은 문제가되지 않습니다.

계산 된 열을 포함하는 인덱스가 있으면 각 식 결과가 테이블의 다른 데이터와 함께 디스크에 저장되므로 DOES는 추가 디스크 공간을 사용합니다.

색인을 원하지 않으면 PERSISTED열 표현식 정의의 끝에 키워드 를 추가하여 표현식을 디스크에 미리 계산하여 CPU를 절약 할 수 있습니다 .

SQL Server 2008 이상에서는 가능하면 필터링 된 솔루션을 사용하십시오!

논쟁

일부 데이터베이스 전문가는 이것을 “서로 게이트 NULL”의 경우로 볼 수 있는데, 이는 분명히 문제가 있습니다 (주로 무언가가 실제 값 인지 또는 누락 된 데이터에 대한 대리 값 인지 결정하려고하는 문제로 인해 문제가 있을 수 있음). NULL이 아닌 대리 값이 미친 것처럼 곱해집니다).

그러나 나는이 사건이 다르다고 생각합니다. 내가 추가하는 계산 열은 아무것도 결정하는 데 사용되지 않습니다. 자체 의미가 없으며 올바르게 정의 된 다른 열에서 별도로 발견되지 않은 정보는 인코딩하지 않습니다. 절대로 선택하거나 사용해서는 안됩니다.

그래서 내 이야기는 이것이 대리 NULL이 아니며, 나는 그것을 고집하고 있다는 것입니다! NULL UNIQUE을 무시 하도록 인덱스를 속이는 것 이외의 다른 목적으로 NULL이 아닌 값을 실제로 원하지 않기 때문에 우리 의 유스 케이스에는 정상적인 대리 NULL 생성에서 발생하는 문제가 없습니다.

그러나 인덱싱 된 뷰를 사용하는 데 아무런 문제가 없지만 사용 요구 사항과 같은 몇 가지 문제가 발생합니다 SCHEMABINDING. 기본 테이블에 새 열을 추가하는 것이 재미 있습니다. 최소한 인덱스를 삭제 한 다음 뷰를 삭제하거나 스키마 바인딩되지 않도록 뷰를 변경해야합니다. SQL Server (2005) (및 이후 버전) (2000) 에서 인덱싱 된 뷰를 만드는 데 필요한 전체 (긴) 요구 사항 목록을 참조하십시오 .

최신 정보

열이 숫자 인 경우 고유 제한 조건을 사용하여 Coalesce충돌이 발생하지 않도록해야 할 수도 있습니다 . 이 경우 몇 가지 옵션이 있습니다. 음수 범위에만 “surrogate NULLs”를, 양수 범위에만 “실제 값”을 배치하기 위해 음수를 사용할 수 있습니다. 대안 적으로, 다음 패턴이 사용될 수있다. 표 Issue(에 IssueID있는 PRIMARY KEY)에는가있을 수도 있고 없을 수도 TicketID있지만 존재하는 경우 고유해야합니다.

ALTER TABLE dbo.Issue ADD TicketUnique
   AS (CASE WHEN TicketID IS NULL THEN IssueID END);
ALTER TABLE dbo.Issue ADD CONSTRAINT UQ_Issue_Ticket_AllowNull
   UNIQUE (TicketID, TicketUnique);

IssueID 1에 티켓 123이 있으면 UNIQUE제약 조건은 값 (123, NULL)입니다. IssueID 2에 티켓이 없으면 티켓이 켜져 있습니다 (NULL, 2). 어떤 생각은이 제약 조건을 테이블의 행에 대해 복제 할 수 없으며 여러 NULL을 허용한다는 것을 보여줍니다.


답변

사용하는 사람들을 위해 마이크로 소프트 SQL 서버 관리자 는 일반적으로 다음 새 인덱스에 대한 색인 속성에서, 왼쪽 패널에서 “필터”를 선택하는 것처럼 고유하지만 Null 허용 인덱스를 만들려면 다음을 입력 한 후, 당신의 고유 인덱스를 만들 수 있습니다 필터 (where 절) 다음과 같이 읽어야합니다.

([YourColumnName] IS NOT NULL)

이것은 MSSQL 2012와 함께 작동합니다


답변

아래의 고유 색인을 적용했을 때 :

CREATE UNIQUE NONCLUSTERED INDEX idx_badgeid_notnull
ON employee(badgeid)
WHERE badgeid IS NOT NULL;

null이 아닌 모든 업데이트 및 삽입이 아래 오류로 실패했습니다.

다음 SET 옵션의 설정이 잘못되어 업데이트에 실패했습니다 : ‘ARITHABORT’.

MSDN 에서 이것을 찾았습니다.

계산 열 또는 인덱싱 된 뷰에서 인덱스를 만들거나 변경할 때는 SET ARITHABORT가 ON이어야합니다. SET ARITHABORT가 OFF이면 계산 열 또는 인덱스 뷰에 인덱스가있는 테이블의 CREATE, UPDATE, INSERT 및 DELETE 문이 실패합니다.

이것이 올바르게 작동하도록하려면이 작업을 수행하십시오.

[데이터베이스]-> 속성-> 옵션-> 기타 옵션-> 기타 —- 산술 중단 활성화-> true를 마우스 오른쪽 버튼으로 클릭하십시오.

코드를 사용 하여이 옵션을 설정할 수 있다고 생각합니다.

ALTER DATABASE "DBNAME" SET ARITHABORT ON

하지만 나는 이것을 테스트하지 않았다


답변

비열 만 선택하는보기를 작성하고보기에서 NULL작성하십시오 UNIQUE INDEX.

CREATE VIEW myview
AS
SELECT  *
FROM    mytable
WHERE   mycolumn IS NOT NULL

CREATE UNIQUE INDEX ux_myview_mycolumn ON myview (mycolumn)

수행해야한다는 점 주 INSERT의와 UPDATE대신 테이블의 뷰의.

INSTEAD OF트리거 로 할 수 있습니다.

CREATE TRIGGER trg_mytable_insert ON mytable
INSTEAD OF INSERT
AS
BEGIN
        INSERT
        INTO    myview
        SELECT  *
        FROM    inserted
END


답변

디자이너도 할 수 있습니다

색인> 속성 을 마우스 오른쪽 버튼으로 클릭 하면이 창이 나타납니다.

포착