SQL Server เลือกคีย์ดัชนีสำหรับการอ้างอิงคีย์ต่างประเทศอย่างไร


9

ฉันทำงานกับฐานข้อมูลดั้งเดิมที่นำเข้าจาก MS Access มีตารางประมาณยี่สิบตารางที่มีคีย์หลักที่ไม่ซ้ำกันและไม่ซ้ำกันซึ่งสร้างขึ้นในระหว่างการเข้าถึง MS> SQL Server อัพเกรด

ตารางเหล่านี้จำนวนมากยังมีดัชนีที่ไม่ซ้ำกันและไม่ทำคลัสเตอร์ซึ่งซ้ำกันของคีย์หลัก

ฉันพยายามทำความสะอาด

แต่สิ่งที่ฉันได้พบคือหลังจากที่ฉันสร้างคีย์หลักใหม่เป็นดัชนีคลัสเตอร์จากนั้นพยายามสร้างคีย์ต่างประเทศคีย์ต่างประเทศคือการอ้างอิงดัชนีเก่าที่ซ้ำกัน (ซึ่งไม่ซ้ำกัน)

ฉันรู้สิ่งนี้เพราะมันจะไม่ให้ฉันปล่อยดัชนีที่ซ้ำกัน

ฉันคิดว่า 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 ไม่อนุญาตให้ใช้ DROP INDEX ที่ชัดเจนในดัชนี 'Parent.IX_Parent' มันถูกใช้สำหรับการบังคับใช้ข้อ จำกัด ของ FOREIGN KEY


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Paul White 9

คำตอบ:


7

(การขาด) เอกสารแสดงให้เห็นว่าพฤติกรรมนี้เป็นรายละเอียดการดำเนินงานและจะไม่ได้กำหนดจึงและอาจมีการเปลี่ยนแปลงได้ตลอดเวลา

สิ่งนี้ตรงกันข้ามกับCREATE FULLTEXT INDEXซึ่งคุณต้องระบุชื่อของดัชนีที่จะแนบไปกับ - AFAIK ไม่มีFOREIGN KEYไวยากรณ์ที่ไม่มีเอกสารที่จะทำสิ่งที่เทียบเท่า (แม้ว่าในทางทฤษฎีอาจมีในอนาคต)

ดังที่กล่าวไว้มันสมเหตุสมผลแล้วที่ SQL Server เลือกดัชนีที่เล็กที่สุดที่จะเชื่อมโยงกับ foreign key หากคุณเปลี่ยนสคริปต์เพื่อสร้างข้อ จำกัด ที่ไม่เหมือนใครCLUSTEREDสคริปต์ "ทำงาน" ใน 2008 R2 แต่พฤติกรรมนั้นยังไม่ได้กำหนดและไม่ควรพึ่งพา

เช่นเดียวกับแอปพลิเคชั่นที่เป็นมรดกส่วนใหญ่คุณเพียงแค่ต้องลงไปที่ nitty-gritty และทำความสะอาดสิ่งต่างๆ


"SQL Server เลือกดัชนีที่เล็กที่สุดที่จะเชื่อมโยงกับ foreign key"ไม่จำเป็นต้องเป็นจริง มีตัวอย่างในคำตอบข้างเคียงโดยที่ 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);

ที่โต๊ะตั้งใจที่จะตารางอ้างอิงTRefT

เพื่อสร้างข้อ จำกัด การอ้างอิงหนึ่งสามารถใช้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 TเทียบกับREFERENCES T (id))

เนื่องจากยังไม่มีดัชนีคีย์Tการเรียกใช้งานคำสั่งเหล่านี้จะสร้างข้อผิดพลาด

คำสั่งแรกส่งคืนข้อผิดพลาดต่อไปนี้:

เกี่ยวกับ 1776 ระดับ 16 สถานะ 0, บรรทัด 4

ไม่มีคีย์หลักหรือตัวเลือกในตารางอ้างอิง 'T' ที่ตรงกับรายการคอลัมน์อ้างอิงในคีย์ต่างประเทศ 'FK_TRef_T_1'

อย่างไรก็ตามคำสั่งที่สองส่งคืนข้อผิดพลาดที่แตกต่างกัน:

เกี่ยวกับ 1773 ระดับ 16 สถานะ 0, บรรทัด 4

foreign key 'FK_TRef_T_2' มีการอ้างอิงโดยนัยถึง object '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_2PK_T

ดังนั้นใช่กับการใช้งานของREFERENCES Tต่างประเทศที่สำคัญไวยากรณ์ของถูกแมปไปคีย์หลักของTRefT

ฉันไม่สามารถค้นหาพฤติกรรมดังกล่าวที่อธิบายไว้ในเอกสารของ SqlServer โดยตรง แต่เฉพาะข่าวสารเกี่ยวกับ 1773 แสดงว่ามันไม่ได้ตั้งใจ การใช้งานดังกล่าวมีความสอดคล้องกับมาตรฐาน SQL ด้านล่างเป็นข้อความที่ตัดตอนมาสั้น ๆ จากมาตรา 11.8 ของ ANSI / ISO 9075-2: 2003

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) หาก <ตารางอ้างอิงและคอลัมน์> ไม่ได้ระบุ <รายการคอลัมน์อ้างอิง> ดังนั้นตัวอธิบายตารางของตารางอ้างอิงจะต้องมีข้อ จำกัด เฉพาะที่ระบุคีย์หลัก ให้คอลัมน์อ้างอิงเป็นคอลัมน์หรือคอลัมน์ที่ระบุโดยคอลัมน์ที่ไม่ซ้ำกันในข้อ จำกัด ที่ไม่ซ้ำกันและให้คอลัมน์อ้างอิง เป็นหนึ่งคอลัมน์ดังกล่าว <ตารางอ้างอิงและคอลัมน์> จะถูกพิจารณาให้ระบุรายการอ้างอิงคอลัมน์โดยนัยที่เหมือนกับรายการคอลัมน์ที่ไม่ซ้ำนั้น
...

Transact-SQL รองรับและขยาย ANSI SQL อย่างไรก็ตามมันไม่สอดคล้องกับ SQL Standard อย่างแน่นอน มีเอกสารชื่อSQL Server Transact-SQL ISO / IEC 9075-2 เอกสารสนับสนุนมาตรฐาน (MS-TSQLISO02 โดยย่อดูที่นี่ ) อธิบายระดับการสนับสนุนที่จัดทำโดย Transact-SQL เอกสารแสดงรายการส่วนขยายและชุดรูปแบบตามมาตรฐาน ตัวอย่างเช่นมันเป็นเอกสารที่MATCHข้อไม่ได้รับการสนับสนุนในการกำหนดข้อ จำกัด อ้างอิง แต่ไม่มีรูปแบบเอกสารที่เกี่ยวข้องกับชิ้นส่วนของมาตรฐานที่อ้างถึง ดังนั้นความคิดเห็นของฉันคือพฤติกรรมที่สังเกตได้รับการบันทึกไว้เพียงพอแล้ว

และด้วยการใช้REFERENCES T (<reference column list>)ไวยากรณ์ดูเหมือนว่า SqlServer จะเลือกดัชนี nonclustered ที่เหมาะสมเป็นอันดับแรกในบรรดาดัชนีของตารางที่ถูกอ้างอิง (อันที่มีค่าน้อยที่สุดindex_idดูเหมือนจะไม่ใช่ขนาดที่เล็กที่สุดตามความเห็นของคำถาม) หรือดัชนีกลุ่มถ้ามัน ชุดและไม่มีดัชนี nonclustered ที่เหมาะสม พฤติกรรมดังกล่าวดูเหมือนจะสอดคล้องกันตั้งแต่ SqlServer 2008 (เวอร์ชั่น 10.0) นี่เป็นเพียงการสังเกตแน่นอนไม่มีการรับประกันในกรณีนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.