열 집합에 대해 고유 한 제약 조건을 적용해야하지만 열의 한 값에만 적용해야하는 상황이 있습니다.
예를 들어 Table (ID, Name, RecordStatus)와 같은 테이블이 있습니다.
RecordStatus는 값 1 또는 2 (활성 또는 삭제됨) 만 가질 수 있으며 동일한 레코드가 여러 개 삭제 되어도 상관 없으므로 RecordStatus = 1 인 경우에만 (ID, RecordStatus)에 대한 고유 제약 조건을 만들고 싶습니다. 신분증.
트리거 작성 외에도 그렇게 할 수 있습니까?
SQL Server 2005를 사용하고 있습니다.
답변
이와 같은 검사 제약을 추가하십시오. 차이점은 Status = 1이고 Count> 0이면 false를 반환한다는 것입니다.
http://msdn.microsoft.com/en-us/library/ms188258.aspx
CREATE TABLE CheckConstraint
(
Id TINYINT,
Name VARCHAR(50),
RecordStatus TINYINT
)
GO
CREATE FUNCTION CheckActiveCount(
@Id INT
) RETURNS INT AS BEGIN
DECLARE @ret INT;
SELECT @ret = COUNT(*) FROM CheckConstraint WHERE Id = @Id AND RecordStatus = 1;
RETURN @ret;
END;
GO
ALTER TABLE CheckConstraint
ADD CONSTRAINT CheckActiveCountConstraint CHECK (NOT (dbo.CheckActiveCount(Id) > 1 AND RecordStatus = 1));
INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2);
INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2);
INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2);
INSERT INTO CheckConstraint VALUES (1, 'No Problems', 1);
INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 1);
INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 2);
-- Msg 547, Level 16, State 0, Line 14
-- The INSERT statement conflicted with the CHECK constraint "CheckActiveCountConstraint". The conflict occurred in database "TestSchema", table "dbo.CheckConstraint".
INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 1);
SELECT * FROM CheckConstraint;
-- Id Name RecordStatus
-- ---- ------------ ------------
-- 1 No Problems 2
-- 1 No Problems 2
-- 1 No Problems 2
-- 1 No Problems 1
-- 2 Oh no! 1
-- 2 Oh no! 2
ALTER TABLE CheckConstraint
DROP CONSTRAINT CheckActiveCountConstraint;
DROP FUNCTION CheckActiveCount;
DROP TABLE CheckConstraint;
답변
보라, 필터링 된 인덱스 . 문서에서 (강조 내) :
필터링 된 인덱스는 잘 정의 된 데이터 하위 집합에서 선택하는 쿼리를 처리하는 데 특히 적합한 최적화 된 비 클러스터형 인덱스입니다. 필터 술어를 사용하여 테이블 행의 일부를 인덱싱합니다. 잘 설계된 필터링 된 인덱스는 전체 테이블 인덱스에 비해 쿼리 성능을 향상시킬뿐만 아니라 인덱스 유지 관리 및 저장 비용을 줄일 수 있습니다.
다음은 고유 인덱스를 필터 조건 자와 결합하는 예입니다.
create unique index MyIndex
on MyTable(ID)
where RecordStatus = 1;
이것은 본질적으로의 고유성을 적용 ID
할 때 RecordStatus
입니다 1
.
해당 인덱스를 만든 후 고유성 위반으로 인해 오류가 발생합니다.
메시지 2601, 수준 14, 상태 1, 줄 13
고유 인덱스가 ‘MyIndex’인 개체 ‘dbo.MyTable’에 중복 키 행을 삽입 할 수 없습니다. 중복 키 값은 (9999)입니다.
참고 : 필터링 된 인덱스는 SQL Server 2008에서 도입되었습니다. 이전 버전의 SQL Server에 대해서는 이 답변을 참조하십시오 .
답변
삭제 된 레코드를 제약 조건이없는 테이블로 이동하고 단일 테이블의 모양을 유지하기 위해 두 테이블의 UNION이있는 뷰를 사용할 수 있습니다.
답변
정말 해키 방식으로 할 수 있습니다 …
테이블에 스키마 바운드 뷰를 만듭니다.
CREATE VIEW 무엇이든 SELECT * FROM Table WHERE RecordStatus = 1
이제 원하는 필드로 뷰에 고유 한 제약 조건을 만듭니다.
스키마 바운드 뷰에 대한 한 가지 참고 사항은 기본 테이블을 변경하는 경우 뷰를 다시 만들어야합니다. 그 때문에 많은 문제가 있습니다.
답변
중복을 허용 할 것이기 때문에 고유 제약 조건이 작동하지 않습니다. 중복 ID를 삽입하기 전에 기존 활성 레코드를 확인하는 INSERT에 대한 RecordStatus 열 및 저장 프로 시저에 대한 검사 제약 조건을 만들 수 있습니다.
답변
Bill이 제안한대로 RecordStatus로 NULL을 사용할 수없는 경우 그의 아이디어를 함수 기반 인덱스와 결합 할 수 있습니다. RecordStatus가 제약 조건에서 고려하려는 값 (그렇지 않으면 RecordStatus)이 아닌 경우 NULL을 반환하는 함수를 만들고 그 위에 인덱스를 만듭니다.
그러면 제약 조건에서 테이블의 다른 행을 명시 적으로 검사 할 필요가 없어 성능 문제가 발생할 수 있다는 이점이 있습니다.
SQL 서버를 전혀 모른다고 말해야하지만 Oracle에서이 접근 방식을 성공적으로 사용했습니다.