SQL Server는 외래 키 참조를위한 인덱스 키를 어떻게 선택합니까?


9

MS Access에서 가져온 레거시 데이터베이스로 작업하고 있습니다. MS Access> SQL Server 업그레이드 중에 생성 된 클러스터되지 않은 고유 한 기본 키가있는 약 20 개의 테이블이 있습니다.

이러한 많은 테이블에는 기본 키의 복제 본인 고유 한 클러스터되지 않은 인덱스도 있습니다.

이것을 정리하려고합니다.

그러나 내가 찾은 것은 기본 키를 클러스터형 인덱스로 다시 만든 다음 외래 키를 다시 작성하려고 시도하면 외래 키가 이전의 중복 인덱스 (고유 한)를 참조하는 것입니다.

중복 인덱스를 삭제하지 않기 때문에 이것을 알고 있습니다.

SQL Server가 항상 기본 키를 선택한다고 생각합니다. SQL Server에는 고유 인덱스와 기본 키 중에서 선택할 수있는 방법이 있습니까?

SQL Server 2008 R2에서 문제를 복제하려면

IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Child') DROP TABLE Child
GO
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Parent') DROP TABLE Parent
GO

-- Create the parent table
CREATE TABLE Parent (ParentID INT NOT NULL IDENTITY(1,1)) 

-- Make the parent table a heap
ALTER TABLE Parent ADD CONSTRAINT PK_Parent PRIMARY KEY NONCLUSTERED (ParentID) 

-- Create the duplicate index on the parent table
CREATE UNIQUE NONCLUSTERED INDEX IX_Parent ON Parent (ParentID) 

-- Create the child table
CREATE TABLE Child  (ChildID  INT NOT NULL IDENTITY(1,1), ParentID INT NOT NULL ) 

-- Give the child table a normal PKey
ALTER TABLE Child ADD CONSTRAINT PK_Child PRIMARY KEY CLUSTERED (ChildID) 

-- Create a foreign key relationship with the Parent table on ParentID
ALTER TABLE Child ADD CONSTRAINT FK_Child FOREIGN KEY (ParentID) 
REFERENCES Parent (ParentID) ON DELETE CASCADE NOT FOR REPLICATION

-- Try to clean this up
-- Drop the foreign key constraint on the Child table
ALTER TABLE Child DROP CONSTRAINT FK_Child

-- Drop the primary key constraint on the Parent table
ALTER TABLE Parent DROP CONSTRAINT PK_Parent

-- Recreate the primary key on Parent as a clustered index
ALTER TABLE Parent ADD CONSTRAINT PK_Parent PRIMARY KEY CLUSTERED (ParentID) 

-- Recreate the foreign key in Child pointing to parent ID
ALTER TABLE Child ADD CONSTRAINT FK_Child FOREIGN KEY (ParentID) 
REFERENCES Parent (ParentID) ON DELETE CASCADE NOT FOR REPLICATION

-- Try to drop the duplicate index on Parent 
DROP INDEX IX_Parent ON Parent 

오류 메시지 :

메시지 3723, 수준 16, 상태 6, 줄 36 인덱스 'Parent.IX_Parent'에는 명시 적 DROP INDEX가 허용되지 않습니다. FOREIGN KEY 제약 조건 적용에 사용되고 있습니다.


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Paul White 9

답변:


7

(부족한) 문서 는이 동작이 구현 세부 사항이므로 정의되지 않았으며 언제든지 변경 될 수 있음을 제안합니다.

이것은 CREATE FULLTEXT INDEX 와 완전히 대조적입니다 . 여기서 첨부 할 인덱스의 이름을 지정해야합니다.-AFAIK, FOREIGN KEY이에 상응하는 문서화되지 않은 구문 은 없습니다 (이론적으로는 미래에있을 수 있습니다).

앞에서 언급했듯이 SQL Server는 외래 키를 연결할 가장 작은 물리적 인덱스를 선택하는 것이 좋습니다. 고유 제한 조건을 작성하도록 스크립트를 변경하면 CLUSTERED2008 R2에서 스크립트가 "작동"합니다. 그러나 그 행동은 여전히 정의 되어 있지 않으며 의존해서는 안됩니다.

대부분의 레거시 응용 프로그램과 마찬가지로 깔끔하고 깔끔한 작업을 수행해야합니다.


"SQL Server는 실제로 외래 키를 연결할 가장 작은 물리적 인덱스를 선택합니다 . " SqlServer가 물리적 크기가 가장 작은 인덱스를 선택하는 인근 답변의 예가 있습니다.
i-one

3

SQL Server에는 고유 인덱스와 기본 키 중에서 선택할 수있는 방법이 있습니까?

외래 키를 만들 때 참조되는 테이블에 대체 키 제약 조건 또는 고유 인덱스가있는 경우 SqlServer가 기본 키를 참조하도록 지시 할 수 있습니다.

기본 키를 참조해야하는 경우 외래 키 정의에 참조되는 테이블 이름 만 지정해야하며 참조되는 열 목록은 생략해야합니다.

ALTER TABLE Child
    ADD CONSTRAINT FK_Child_Parent FOREIGN KEY (ParentID)
        -- omit key columns of the referenced table
        REFERENCES Parent /*(ParentID)*/;

자세한 내용은 아래를 참조하십시오.


다음 설정을 고려하십시오.

CREATE TABLE T (id int NOT NULL, a int, b int, c uniqueidentifier, filler binary(1000));
CREATE TABLE TRef (tid int NULL);

여기서 table TRef은 table 을 참조하려고합니다 T.

참조 제한 조건을 작성하려면 ALTER TABLE다음 두 가지 대안으로 명령을 사용할 수 있습니다 .

ALTER TABLE TRef
    ADD CONSTRAINT FK_TRef_T_1 FOREIGN KEY (tid) REFERENCES T (id);

ALTER TABLE TRef
    ADD CONSTRAINT FK_TRef_T_2 FOREIGN KEY (tid) REFERENCES T;

두 번째 경우에는 참조되는 테이블의 열이 지정되지 않습니다 ( REFERENCES Tvs REFERENCES T (id)).

T아직 키 인덱스가 없기 때문에 이러한 명령을 실행하면 오류가 발생합니다.

첫 번째 명령은 다음 오류를 반환합니다.

메시지 1776, 수준 16, 상태 0, 줄 4

참조 테이블 'T'에는 외래 키 'FK_TRef_T_1'의 참조 열 목록과 일치하는 기본 또는 후보 키 가 없습니다 .

그러나 두 번째 명령은 다른 오류를 반환합니다.

메시지 1773, 수준 16, 상태 0, 줄 4

외래 키 'FK_TRef_T_2'에는 기본 키가 정의 되지 않은 'T'개체에 대한 암시 적 참조가 있습니다.

첫 번째 경우에는 기본 키 또는 후보 키인 반면 두 번째 경우에는 기본 키만 있습니다.

SqlServer가 기본 키 이외의 다른 명령을 두 번째 명령과 함께 사용할지 여부를 확인하십시오.

고유 인덱스와 고유 키를 추가하면 T:

CREATE UNIQUE INDEX IX_T_1 on T(id) INCLUDE (filler);
CREATE UNIQUE INDEX IX_T_2 on T(id) INCLUDE (c);
CREATE UNIQUE INDEX IX_T_3 ON T(id) INCLUDE (a, b);

ALTER TABLE T
    ADD CONSTRAINT UQ_T UNIQUE CLUSTERED (id);

FK_TRef_T_1만들기 명령은 성공하지만 FK_TRef_T_2여전히 메시지 1773으로 인해 만들기 명령이 실패합니다.

마지막으로 기본 키를 추가하면 T:

ALTER TABLE T
    ADD CONSTRAINT PK_T PRIMARY KEY NONCLUSTERED (id);

FK_TRef_T_2창조를 위한 명령이 성공합니다.

테이블의 T외래 키가 참조 하는 테이블의 인덱스를 확인하십시오 TRef.

select
    ix.index_id,
    ix.name as index_name,
    ix.type_desc as index_type_desc,
    fk.name as fk_name
from sys.indexes ix
    left join sys.foreign_keys fk on
        fk.referenced_object_id = ix.object_id
        and fk.key_index_id = ix.index_id
        and fk.parent_object_id = object_id('TRef')
where ix.object_id = object_id('T');

이것은 다음을 반환합니다.

index_id  index_name  index_type_desc   fk_name
--------- ----------- ----------------- ------------
1         UQ_T        CLUSTERED         NULL
2         IX_T_1      NONCLUSTERED      FK_TRef_T_1
3         IX_T_2      NONCLUSTERED      NULL
4         IX_T_3      NONCLUSTERED      NULL
5         PK_T        NONCLUSTERED      FK_TRef_T_2

FK_TRef_T_2해당하는 것을 참조하십시오 PK_T.

예, REFERENCES T구문 을 사용하면 외래 키 of TRef가 기본 키 of에 매핑됩니다 T.

SqlServer 설명서에 설명 된 이러한 동작을 직접 찾을 수 없었지만 전용 메시지 1773은 우연이 아니라고 제안합니다. 이러한 구현은 SQL 표준을 준수 할 가능성이 있으며 ANSI / ISO 9075-2 : 2003의 섹션 11.8에서 발췌 한 내용은 다음과 같습니다.

11 스키마 정의 및 조작

11.8 <참조 구속 조건 정의>

기능
참조 제한 조건을 지정하십시오.

체재

<referential constraint definition> ::=
    FOREIGN KEY <left paren> <referencing columns> <right paren>
        <references specification>

<references specification> ::=
    REFERENCES <referenced table and columns>
    [ MATCH <match type> ]
    [ <referential triggered action> ]
...

구문 규칙
...
3) 사례 :
...
b) <참조 테이블 및 열>이 <참조 열 목록>을 지정하지 않으면 참조 된 테이블의 테이블 디스크립터는 PRIMARY KEY를 지정하는 고유 제한 조건을 포함해야합니다. 하자 참조 된 열은 열 또는 고유 제한 조건에 고유 열로 식별 열 수 및하자 참조 된 열은 하나의 열합니다. <참조 테이블 및 열>은 <고유 열 목록>과 동일한 <참조 열 목록>을 암시 적으로 지정하는 것으로 간주됩니다.
...

Transact-SQL은 ANSI SQL을 지원하고 확장합니다. 그러나 SQL 표준을 정확히 준수하지는 않습니다. 라는 이름의 문서가 9075-2 표준 지원 문서 SQL 서버 거래-SQL ISO / IEC (짧은 MS-TSQLISO02를 참조하십시오 여기에 거래-SQL에서 제공하는 지원의 수준을 설명). 이 문서에는 표준 확장 및 변형이 나와 있습니다. 예를 들어 MATCH, 참조 제한 조건 정의에서 해당 절이 지원되지 않음을 문서화합니다 . 그러나 인용 된 표준과 관련된 문서화 된 변형은 없습니다. 따라서 관찰 된 행동이 충분히 문서화되어 있다고 생각합니다.

그리고 REFERENCES T (<reference column list>)구문 을 사용 하면 SqlServer가 참조되는 테이블의 인덱스 중에서 가장 적합한 비 클러스터형 인덱스 ( index_id질문 의견에서 가정 한 것처럼 가장 작은 물리적 크기가 아닌 것) 또는 클러스터 된 인덱스를 선택하는 것 같습니다 적합한 비 클러스터형 인덱스가 없습니다. 이러한 동작은 SqlServer 2008 (버전 10.0) 이후 일관된 것으로 보입니다. 이것은 물론 관찰 일뿐 이며이 경우 보장 할 수 없습니다.

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