Birincil anahtar değeri neden değişsin?


18

Son zamanlarda ROWGUID kavramını araştırıyorum ve bu soruya rastladım . Bu cevap bir fikir verdi, ancak birincil anahtar değerini değiştirmekten bahsederek beni farklı bir tavşan deliğine götürdü.

Benim anlayışım her zaman birincil bir anahtarın değişmez olması gerektiğiydi ve bu cevabı okuduğumdan beri yaptığım araştırma sadece en iyi uygulama ile aynı olan cevapları verdi.

Hangi koşullarda kayıt oluşturulduktan sonra birincil anahtar değerinin değiştirilmesi gerekir?


7
Değişmez olmayan bir birincil anahtar seçildiğinde?
ypercubeᵀᴹ

2
Şimdiye kadar aşağıdaki cevapların sadece küçük bir nit. Birincil anahtar da kümelenmiş dizin olmadıkça birincil anahtardaki bir değeri değiştirmek o kadar büyük bir anlaşma değildir. Yalnızca kümelenmiş dizinin değerleri değişirse gerçekten önemlidir.
Kenneth Fisher

6
@KennethFisher veya diğer veya aynı tablodaki bir (veya daha fazla) FK tarafından atıfta bulunuluyorsa ve bir değişiklik birçok (muhtemelen milyonlarca veya milyarlarca) satıra basamaklandırılmalıdır.
ypercubeᵀᴹ

9
Skype'a sorun. Birkaç yıl önce kaydolduğumda, kullanıcı adımı yanlış yazdım (soyadımdan bir mektup bıraktım). Düzeltmek için birçok kez denedim, ancak birincil anahtar için kullanıldığından ve değiştirmeyi desteklemedikleri için değiştiremediler. Bu, müşterinin birincil anahtarın değiştirilmesini istediği bir örnektir, ancak Skype bunu desteklemedi. Onlar olabilir onlar (ya da daha iyi bir tasarım oluşturabilirsiniz) istiyorsa bu değişikliği destekleyen, ancak buna izin yerde hiçbir şey bulunmuyor. Yani kullanıcı adım hala yanlış.
Aaron Bertrand

3
Tüm gerçek dünya değerleri değişebilir (çeşitli nedenlerle). Bu vekil / sentetik anahtarlar için orijinal motivasyonlardan biriydi: asla değişmemeye güvenilebilecek yapay değerler üretebilmek.
RBarryYoung

Yanıtlar:


24

Bir kişinin adını birincil anahtar olarak kullanıyorsanız ve adı değiştiyse, birincil anahtarı değiştirmeniz gerekir. ON UPDATE CASCADETemel anahtarla yabancı anahtar ilişkileri olan tüm ilgili tablolara geçişi esas aldığı için kullanılan budur .

Örneğin:

USE tempdb;
GO

CREATE TABLE dbo.People
(
    PersonKey VARCHAR(200) NOT NULL
        CONSTRAINT PK_People
        PRIMARY KEY CLUSTERED
    , BirthDate DATE NULL
) ON [PRIMARY];

CREATE TABLE dbo.PeopleAKA
(
    PersonAKAKey VARCHAR(200) NOT NULL
        CONSTRAINT PK_PeopleAKA
        PRIMARY KEY CLUSTERED
    , PersonKey VARCHAR(200) NOT NULL
        CONSTRAINT FK_PeopleAKA_People
        FOREIGN KEY REFERENCES dbo.People(PersonKey)
        ON UPDATE CASCADE
) ON [PRIMARY];

INSERT INTO dbo.People(PersonKey, BirthDate)
VALUES ('Joe Black', '1776-01-01');

INSERT INTO dbo.PeopleAKA(PersonAKAKey, PersonKey)
VALUES ('Death', 'Joe Black');

Her SELECTiki tabloya karşı A :

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonKey = pa.PersonKey;

İadeler:

resim açıklamasını buraya girin

PersonKeySütunu güncellersek ve yeniden çalıştırırsak SELECT:

UPDATE dbo.People
SET PersonKey = 'Mr Joe Black'
WHERE PersonKey = 'Joe Black';

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonKey = pa.PersonKey;

görürüz:

resim açıklamasını buraya girin

Yukarıdaki UPDATEifadenin planına baktığımızda, her iki tablonun, aşağıdaki gibi tanımlanan yabancı anahtar sayesinde tek bir güncelleme ifadesiyle güncellendiğini açıkça görüyoruz ON UPDATE CASCADE:

resim açıklamasını buraya girin daha net görmek için yukarıdaki resme tıklayın

Son olarak, geçici tablolarımızı temizleyeceğiz:

DROP TABLE dbo.PeopleAKA;
DROP TABLE dbo.People;

Yedek anahtarlar kullanarak bunu yapmanın tercih edilen 1 yolu:

USE tempdb;
GO

CREATE TABLE dbo.People
(
    PersonID INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_People
        PRIMARY KEY CLUSTERED
    , PersonName VARCHAR(200) NOT NULL
    , BirthDate DATE NULL
) ON [PRIMARY];

CREATE TABLE dbo.PeopleAKA
(
    PersonAKAID INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_PeopleAKA
        PRIMARY KEY CLUSTERED
    , PersonAKAName VARCHAR(200) NOT NULL
    , PersonID INT NOT NULL
        CONSTRAINT FK_PeopleAKA_People
        FOREIGN KEY REFERENCES dbo.People(PersonID)
        ON UPDATE CASCADE
) ON [PRIMARY];

INSERT INTO dbo.People(PersonName, BirthDate)
VALUES ('Joe Black', '1776-01-01');

INSERT INTO dbo.PeopleAKA(PersonID, PersonAKAName)
VALUES (1, 'Death');

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonID = pa.PersonID;

UPDATE dbo.People
SET PersonName = 'Mr Joe Black'
WHERE PersonID = 1;

Tamlık için, güncelleme ifadesi planı çok basittir ve anahtarları taşımak için bir avantaj gösterir, yani doğal anahtar senaryosunda anahtarı içeren her satırın aksine yalnızca tek bir satırın güncellenmesi gerekir :

resim açıklamasını buraya girin

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonID = pa.PersonID;

DROP TABLE dbo.PeopleAKA;
DROP TABLE dbo.People;

SELECTYukarıdaki iki ifadeden elde edilen çıktı :

resim açıklamasını buraya girin

Esasen, sonuç yaklaşık olarak aynıdır. Önemli bir fark, geniş doğal anahtarın yabancı anahtarın oluştuğu her tabloda tekrarlanmamasıdır. VARCHAR(200)Örneğimde, kişinin adını tutmak için VARCHAR(200) her yerde kullanılmasını gerektiren bir sütun kullanıyorum . Çok sayıda satır ve yabancı anahtarı içeren çok sayıda tablo varsa, bu çok fazla boşa bellek katar. Not, çoğu insan disk alanının aslında ücretsiz olacak kadar ucuz olduğunu söyledi çünkü boşa harcanan disk hakkında konuşmuyorum. Bununla birlikte, bellek pahalıdır ve sevilmeyi hak eder. Anahtar için 4 baytlık bir tamsayı kullanmak, yaklaşık 15 karakterlik ortalama ad uzunluğunu düşündüğünüzde büyük miktarda bellek tasarrufu sağlayacaktır.

Anahtarların nasıl ve neden değişebileceği sorusuna teğet , yedek anahtarlar üzerinde doğal anahtarların neden seçilmesiyle ilgili soru, bu da özellikle performansın bir tasarım hedefi olduğu ilginç ve belki de daha önemli bir sorudur. Benim soruya bakın burada bu konuda.


1 - http://weblogs.sqlteam.com/mladenp/archive/2009/10/06/Why-I-prefer-surrogate-keys-instead-of-natural-keys-in.aspx


3
CASCADE'den (belirli senaryolarda sorunları olan) kaçınmak için FK sütunlarını boş bırakabilirsiniz, böylece PK'yı değiştirmeniz gerekirse, ilgili satırları NULL (çok sayıda, yığın halinde veya tablo olarak) olarak güncelleyebilirsiniz , çok fazla tablo varsa veya her ikisi birden varsa) ve ardından PK değerini değiştirin ve ardından FK'leri tekrar değiştirin.
Aaron Bertrand

8

PK'nız olarak doğal ve / veya değişebilen bir anahtarı kullanabilmenize rağmen, sorunlara yol açan deneyimime göre, genellikle bu koşulları karşılayan bir PK kullanımı ile önlenebilir:

 Guaranteed Unique, Always Exists, Immutable, and Concise.

Örneğin, ABD'deki birçok şirket, sistemlerinde Sosyal Güvenlik Numaralarını kişisel kimlik numaraları (ve PK'lar) olarak kullanmaya çalışmaktadır. Daha sonra aşağıdaki sorunlarla karşılaşırlar - onarılması gereken birden fazla kayda yol açan veri giriş hataları, SSN'si olmayan kişiler, SSN'si hükümet tarafından değiştirilen kişiler, yinelenen SSN'leri olan kişiler.

Bu senaryoların her birini gördüm. Müşterilerinin "sadece bir sayı" olmasını istemeyen şirketler gördüm, bu da PK'larının 'ilk + orta + son + DOB + zip' veya benzer bir saçmalık olduğu anlamına geliyordu. Benzersizliği neredeyse garanti edecek kadar alan ekleseler de, sorguları korkunçtu ve bu alanlardan herhangi birini güncellemek veri tutarlılığı sorunlarını izlemek anlamına geliyordu.

Deneyimlerime göre, veritabanının kendisi tarafından oluşturulan bir PK neredeyse her zaman daha iyi bir çözümdür.

Bu makaleyi ek işaretçiler için öneririm: http://www.agiledata.org/essays/keys.html


6
Yanıtınızda atıfta bulunulan Scott Ambler makalesinden iyi bir tavsiye: "Bazıları size her zaman doğal anahtarlar kullanmanız gerektiğini söylerken, diğerleri size her zaman yedek anahtar kullanmanız gerektiğini söyleyecektir. "veri dinlerinin" önyargılarını sizinle paylaşmaktan çok daha fazlasını yapıyorlar. Gerçek şu ki, doğal ve yedek anahtarların her birinin avantajları ve dezavantajları vardır ve hiçbir strateji tüm durumlar için mükemmel değildir. "
nvogel

7

Senkronizasyon söz konusu olduğunda birincil anahtar değiştirilebilir. Bağlantısı kesilmiş bir istemciniz olduğunda ve belirli aralıklarla verileri sunucuyla senkronize ettiğinde bu durum söz konusu olabilir.

Birkaç yıl önce, yerel makinedeki tüm olay verilerinin -1, -2 vb. Gibi negatif satır Kimlikleri olduğu bir sistem üzerinde çalıştım. Veriler sunucu ile senkronize edildiğinde, sunucudaki satır kimliği istemcisi. Diyelim ki sunucudaki bir sonraki satır kimliği 58 idi. Sonra -1, 58, -2 59 ve benzeri olur. Bu satır kimliği değişikliği, yerel makinedeki tüm alt FK kayıtlarına basamaklandırılır. Mekanizma, daha önce hangi kayıtların senkronize edildiğini belirlemek için de kullanıldı.

Bunun iyi bir tasarım olduğunu söylemiyorum, ancak zamanla değişen birincil anahtarın bir örneği.


5

PRIMARY KEYDüzenli olarak değiştirmeyi içeren herhangi bir tasarım felaket için bir reçetedir. Bunu değiştirmenin tek iyi nedeni, daha önce iki ayrı veritabanının birleşmesidir.

@ MaxVernon tarafından işaret edildiği gibi, zaman zaman değişiklikler meydana gelebilir - o zaman kullanın ON UPDATE CASCADE, ancak günümüzde sistemlerin çoğu vekil olarak bir kimlik kullanmaktadır PRIMARY KEY.

Joe Celko ve Fabian Pascal (takip etmeye değer bir site) gibi puristler , yedek anahtarların kullanımına katılmıyorlar, ancak bence bu belirli savaşı kaybettiler.


3

Kararlılık, bir anahtar için istenen bir özelliktir, ancak mutlak bir kural değil, göreceli bir şeydir. Uygulamada, anahtarların değerlerini değiştirmek genellikle yararlıdır. İlişkisel terimlerle veriler yalnızca (süper) tuşları ile tanımlanabilir. Belirli bir tabloda yalnızca bir anahtar varsa, A) bir anahtar değerini değiştirme veya B) bir tablodaki satır kümesini, diğer anahtar değerlerini içeren bazı benzer veya farklı satır kümesiyle değiştirme temel olarak mantıktan ziyade anlambilim meselesi.

Daha ilginç bir örnek, bu anahtarlardan bir veya daha fazlasının değerlerinin diğer anahtar değerlerine göre değişmesi gerekebilecek birden fazla anahtar içeren bir tablo durumudur. İki anahtarlı bir Çalışan tablosu örneğini alın: LoginName ve Badge Number. İşte o tablodan bir örnek satır:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS     |47832   |
+---------+--------+

ZoeS rozetini kaybederse, yeni bir rozet tahsis edilir ve yeni bir rozet numarası alır:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS     |50282   |
+---------+--------+

Daha sonra, giriş adını değiştirmeye karar verebilir:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZSmith   |50282   |
+---------+--------+

Her iki anahtar değer de birbiriyle ilişkili olarak değişti. Birinin "birincil" olarak kabul edilen bir fark yaratması gerekmediğini unutmayın.

Pratikte "değişmezlik", yani kesinlikle hiçbir zaman bir değeri değiştirmemek, ulaşılamaz veya en azından doğrulanması imkansızdır. Değişimin bir fark yarattığı ölçüde, en güvenli yol muhtemelen herhangi bir anahtarın (veya herhangi bir özelliğin) değişmesi gerekebileceğini varsaymaktır.


Aşağıdaki ifade nedeniyle yorumunuzu reddetti: "Pratikte" değişmezlik ", yani kesinlikle hiçbir zaman bir değeri değiştirmemek, ulaşılamaz veya en azından doğrulamak imkansız." Değişmezlik mümkündür ve vekil anahtarları kullanmanın en önemli nedenlerinden biridir.
Byron Jones

3
Gelecek hafta veya 10 yıl içinde birisinin anahtar değerini değiştirmeyeceğini nasıl bilebilirsiniz? Onlar olmayacağını varsayabilirsin ama gerçekçi bir şekilde bunun olmasını önleyemezsin (eğer tek başına sorumluysan, sanırım herkesi süreklilik içinde tutmak için engeller koyabilirsin, ama bu bir uç dava gibi görünüyor). Asıl önemli olan, değişikliklerin çok seyrek olması, asla gerçekleşmeyecekleri değil.
nvogel

3

İlginçtir ki, ROWGUID türüyle ilgili bağlantılı soru kendi kullanım durumunu sağlar: senkronize edilmesi gereken veritabanlarında birincil anahtarlar çakıştığında. Uzlaştırılması gereken iki veritabanınız varsa ve bunlar birincil anahtarlar için sıralar kullanıyorsa, benzersiz kalması için anahtarlardan birinin değişmesini istersiniz.

İdeal bir dünyada bu asla olmazdı. Başlamak için birincil anahtarlar için GUID'leri kullanırsınız. Gerçekçi olmakla birlikte, tasarım yapmaya başladığınızda dağıtılmış bir veritabanınız bile olmayabilir ve GUID'lere dönüştürmek, temel güncelleştirmeyi uygulamaktan daha yüksek bir etki olarak kabul edildiğinden, dağıtımı yapmak için aşağıda öncelik verilen bir çaba olabilir. Tamsayı anahtarlarına bağlı büyük bir kod tabanınız varsa ve GUID'e dönüştürmek için büyük bir düzeltme gerektiriyorsa bu gerçekleşebilir. Ayrıca, seyrek GUID'lerin (birbirine çok yakın olmayan GUID'lerin, gerektiğinde rastgele oluşturursanız gerçekleşen) belirli dizin türleri için de sorunlara neden olabileceği gerçeği de vardır, bu da kullanmaktan kaçınmak istediğiniz anlamına gelir onları birincil anahtarlar olarak ( Byron Jones tarafından belirtilmiştir ).


0

Olası bir senaryo, benzersiz kimliğe sahip bağlı kuruluşlarınız olduğunu ve benzersiz başlangıç ​​karakterine sahip oldukları için bağlı kuruluşlar arasında kopyalanmayacağını bildiğinizi varsayalım. Bağlı kuruluşlar ana tabloya veri yükler. Kayıtlar işlenir ve ardından bir ana kimlik atanır. Kullanıcıların henüz işlenmemiş olsalar bile yüklenir yüklenmez kayıtlara erişmeleri gerekir. Ana kimliğin işlenen siparişe dayalı olmasını istiyorsunuz ve her zaman kayıtların yüklenme sırasına göre işlem görmeyeceksiniz. Biraz uydurma biliyorum.


-1

Birisinin Birincil Anahtar olarak Ulusal Sigorta Numarasını (NIN) seçmesi ve bir şekilde bir operatörün yanlış NIN ile bir satır eklemesi gibi bir durumu düşünün. Değeri ekledikten sonra, hatayı düzeltmenin iki yolu vardır:

  1. Yanlış kaydı silin ve yeni bir kayıt ekleyin
  2. Değeri doğru olarak güncelleyin ve bu sütunda bir başvuru bütünlüğü kısıtlaması varsa, Cascade Güncellemede kullanın
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.