SQL Server yabancı anahtar başvurusu için bir dizin anahtarını nasıl seçer?


9

MS Access'ten alınan eski bir veritabanı ile çalışıyorum. MS Access> SQL Server yükseltmesi sırasında oluşturulan, kümelenmemiş, benzersiz birincil anahtarlara sahip yaklaşık yirmi tablo vardır.

Bu tabloların çoğunda, birincil anahtarın kopyaları olan benzersiz, kümelenmemiş dizinler de vardır.

Bunu temizlemeye çalışıyorum.

Ama bulduğum şey, birincil anahtarları kümelenmiş dizinler olarak yeniden oluşturduktan ve sonra yabancı anahtarı yeniden oluşturmaya çalıştıktan sonra, yabancı anahtar eski, yinelenen dizine (benzersiz) başvuruyor.

Bunu biliyorum çünkü yinelenen dizinleri bırakmama izin vermiyor.

Varsa SQL Server her zaman birincil bir anahtar seçeceğini düşünürdüm. SQL Server'ın benzersiz bir dizin ve birincil anahtar arasında seçim yapma yöntemi var mı?

Sorunu çoğaltmak için (SQL Server 2008 R2'de):

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 

Hata mesajı:

Msg 3723, Seviye 16, Durum 6, Satır 36 'Parent.IX_Parent' dizininde açık bir DROP INDEX'e izin verilmiyor. FOREIGN KEY kısıtlama uygulaması için kullanılıyor.


Yorumlar uzun tartışmalar için değildir; bu sohbet sohbete taşındı .
Paul White 9

Yanıtlar:


7

Belgelerin (eksikliği) bu davranışın bir uygulama detayı olduğunu ve bu nedenle tanımsız olduğunu ve herhangi bir zamanda değişebileceğini göstermektedir.

Bu, eklenecek bir dizinin adını belirtmeniz gereken CREATE FULLTEXT INDEX ile tam bir zıttır - AFAIK, FOREIGN KEYeşdeğerini yapmak için belgesiz sözdizimi yoktur (teorik olarak gelecekte olabilir).

Belirtildiği gibi, SQL Server'ın yabancı anahtarı ilişkilendirmek için en küçük fiziksel dizini seçmesi mantıklıdır. Komut dosyasını benzersiz bir kısıtlama oluşturmak için değiştirirseniz, komut CLUSTEREDdosyası 2008 R2'de "çalışır". Ancak bu davranış hala tanımlanmamıştır ve buna güvenilmemelidir.

Çoğu eski uygulamada olduğu gibi, sadece cesur ve temiz şeylere inmeniz gerekir.


"SQL Server, yabancı anahtarın ilişkilendirileceği en küçük fiziksel dizini seçer" aslında değil. Komşu cevapta SqlServer'ın en küçük fiziksel boyutta olmayan bir dizin seçtiği bir örnek var.
i-one

3

SQL Server'ın benzersiz bir dizin ve birincil anahtar arasında seçim yapma yöntemi var mı?

En azından yabancı anahtar oluşturulurken ve başvurulan tabloda alternatif anahtar kısıtlamaları veya benzersiz dizinler mevcut olduğunda SqlServer'ı birincil anahtara başvuruda bulunmak mümkündür.

Birincil anahtara başvurulması gerekiyorsa, yabancı anahtar tanımında yalnızca başvurulmakta olan tablonun adı belirtilmelidir ve başvuruda bulunulan sütunların listesi atlanmalıdır:

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

Daha fazla ayrıntı aşağıda.


Aşağıdaki kurulumu düşünün:

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

burada tablonun TRefreferans tablosuna bakması amaçlanmaktadır T.

Referans kısıtlaması oluşturmak ALTER TABLEiçin iki alternatifli komut kullanılabilir :

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;

ikinci durumda başvurulan tablonun hiçbir sütununun ( REFERENCES Tkarşı REFERENCES T (id)) belirtilmediğine dikkat edin .

THenüz anahtar dizini bulunmadığından , bu komutların yürütülmesi hatalar üretecektir.

İlk komut aşağıdaki hatayı döndürür:

Msg 1776, Seviye 16, Durum 0, Satır 4

Referans verilen 'T' tablosunda, 'FK_TRef_T_1' yabancı anahtarındaki referans sütun listesiyle eşleşen birincil veya aday anahtarlar yok .

Ancak ikinci komut farklı bir hata döndürür:

Msg 1773, Seviye 16, Durum 0, Satır 4

Yabancı anahtar 'FK_TRef_T_2', üzerinde birincil anahtar tanımlanmamış 'T' nesnesine örtük bir referansa sahip .

birinci durumda beklentinin birincil veya aday anahtarlar olduğunu , ikinci durumda beklentinin ise yalnızca birincil anahtar olduğunu görün .

SqlServer'ın ikinci komutla birincil anahtar dışında bir şey kullanıp kullanmayacağını kontrol edelim.

Şuna bazı benzersiz dizinler ve benzersiz anahtar eklersek 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_1oluşturma komutu başarılı, ancak FK_TRef_T_2oluşturma komutu Msg 1773 ile hala başarısız oluyor.

Son olarak, üzerine birincil anahtar eklersek T:

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

FK_TRef_T_2oluşturma komutu başarılı.

Tablonun hangi dizinlerine Treferans olarak tablonun yabancı anahtarları ile bakalım 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');

bu döndürür:

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_2karşılık gelen bakın PK_T.

Yani, evet, REFERENCES Tsözdizimi kullanımı ile yabancı anahtarın TRefbirincil anahtarına eşlenir T.

Doğrudan SqlServer belgelerinde açıklanan bu tür bir davranış bulamadı, ancak özel Msg 1773 yanlışlıkla olmadığını gösteriyor. Muhtemelen böyle bir uygulama SQL Standardına uygunluk sağlar, aşağıda ANSI / ISO 9075-2: 2003'ün 11.8 bölümünden kısa alıntıdır.

Şema tanımı ve manipülasyonu

11.8 <başvuru kısıtlaması tanımı>

İşlev
Bir referans kısıtlaması belirleyin.

Biçim

<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> ]
...

Sözdizimi Kuralları
...
3) Durum:
...
b) <Başvurulan tablo ve sütunlar> bir <başvuru sütun listesi> belirtmezse, başvurulan tablonun tablo tanımlayıcısı PRIMARY KEY'i belirten benzersiz bir kısıtlama içermelidir. Let başvurulan sütunları sütun veya benzersiz kısıtlama benzersiz kolonlar tarafından belirlenen sütun olmak ve izin başvurulan sütun böyle bir sütun. <Referans verilen tablo ve sütunlar>, bu <benzersiz sütun listesi> ile aynı olan bir <referans sütun listesi> örtük olarak belirtilmiş kabul edilecektir.
...

Transact-SQL, ANSI SQL'i destekler ve genişletir. Ancak SQL Standardına tam olarak uymaz. Transact-SQL tarafından sağlanan destek düzeyini açıklayan SQL Server Transact-SQL ISO / IEC 9075-2 Standartları Destek Belgesi (kısaca MS-TSQLISO02, buraya bakın ) adlı bir belge vardır . Belge, standartlara göre uzantıları ve varyasyonları listeler. Örneğin, MATCHyan tümcenin başvuru kısıtlaması tanımında desteklenmediğini belgeler . Ancak belirtilen standart parça ile ilgili belgelenmiş varyasyon yoktur. Benim görüşüme göre gözlemlenen davranış yeterince belgeleniyor.

REFERENCES T (<reference column list>)Sözdizimi kullanıldığında, SqlServer başvurulan tablonun dizinleri arasında ilk uygun kümelenmemiş dizini (en az index_idgörünüşte olan, soru yorumlarında varsayılan olarak en küçük fiziksel boyuta sahip olanı değil) veya kümelenmiş dizini seçiyor gibi görünüyor. uygun ve kümelenmemiş indeksler yoktur. Bu davranış, SqlServer 2008'den (sürüm 10.0) beri tutarlı görünüyor. Bu sadece gözlemdir, bu durumda hiçbir garanti yoktur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.