เหตุใดค่าคีย์หลักจึงเปลี่ยนไป


18

ฉันได้ค้นคว้าแนวคิดของ ROWGUID เมื่อเร็ว ๆ นี้และได้พบกับคำถามนี้ คำตอบนี้ให้ข้อมูลเชิงลึก แต่ทำให้ฉันลงหลุมกระต่ายที่แตกต่างกับการกล่าวถึงการเปลี่ยนค่าคีย์หลัก

ความเข้าใจของฉันคือกุญแจหลักที่ไม่ควรเปลี่ยนและการค้นหาของฉันตั้งแต่การอ่านคำตอบนี้ได้ให้คำตอบที่สะท้อนการปฏิบัติที่ดีที่สุดเท่านั้น

ค่าคีย์หลักจะต้องมีการเปลี่ยนแปลงภายใต้สถานการณ์ใดหลังจากสร้างเรคคอร์ดแล้ว


7
เมื่อเลือกคีย์หลักที่ไม่เปลี่ยนรูปหรือไม่
ypercubeᵀᴹ

2
เพียงเล็กน้อยไปยังคำตอบด้านล่างทั้งหมด การเปลี่ยนค่าในคีย์หลักนั้นไม่ใช่ข้อตกลงที่ยิ่งใหญ่เว้นแต่ว่าคีย์หลักจะเป็นดัชนีคลัสเตอร์ มันสำคัญจริงๆถ้าค่าของดัชนีคลัสเตอร์เปลี่ยนแปลง
Kenneth Fisher

6
@KennethFisher หรือถ้ามีการอ้างอิงโดย FK หนึ่ง (หรือหลายคน) ในตารางอื่น ๆ หรือตารางเดียวกันและการเปลี่ยนแปลงจะต้องเรียงซ้อนกันเป็นแถว (อาจเป็นล้านหรือพันล้าน)
ypercubeᵀᴹ

9
ถาม Skype เมื่อฉันสมัครใช้งานเมื่อหลายปีก่อนฉันพิมพ์ชื่อผู้ใช้ไม่ถูกต้อง (เหลือจดหมายจากนามสกุล) ฉันพยายามแก้ไขให้ถูกต้องหลายครั้ง แต่พวกเขาไม่สามารถเปลี่ยนแปลงได้เพราะใช้สำหรับคีย์หลักและพวกเขาไม่สนับสนุนการเปลี่ยนแปลง นี่เป็นอินสแตนซ์ที่ลูกค้าต้องการเปลี่ยนรหัสหลัก แต่ Skype ไม่ได้ให้การสนับสนุน พวกเขาสามารถรองรับการเปลี่ยนแปลงนั้นได้หากพวกเขาต้องการ (หรือพวกเขาสามารถสร้างการออกแบบที่ดีกว่า) แต่ขณะนี้ไม่มีอะไรที่จะอนุญาต ดังนั้นชื่อผู้ใช้ของฉันยังคงไม่ถูกต้อง
Aaron Bertrand

3
ค่าจริงทั้งหมดสามารถเปลี่ยนแปลงได้ (สำหรับสาเหตุที่หลากหลาย) นี่คือแรงจูงใจดั้งเดิมสำหรับคีย์ตัวแทน / สังเคราะห์: เพื่อให้สามารถสร้างค่าประดิษฐ์ที่สามารถไว้วางใจได้ว่าจะไม่เปลี่ยนแปลง
RBarryYoung

คำตอบ:


24

หากคุณใช้ชื่อของบุคคลเป็นคีย์หลักและเปลี่ยนชื่อคุณจะต้องเปลี่ยนคีย์หลัก นี่คือสิ่งที่ON UPDATE CASCADEใช้สำหรับเป็นหลักเพราะเรียงซ้อนการเปลี่ยนแปลงลงในตารางที่เกี่ยวข้องทั้งหมดที่มีความสัมพันธ์กับคีย์ต่างประเทศกับคีย์หลัก

ตัวอย่างเช่น:

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');

A SELECTกับทั้งสองตาราง:

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

ผลตอบแทน:

ป้อนคำอธิบายรูปภาพที่นี่

หากเราอัปเดตPersonKeyคอลัมน์และเรียกใช้งานอีกครั้ง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;

ที่เราเห็น:

ป้อนคำอธิบายรูปภาพที่นี่

เมื่อดูที่แผนสำหรับUPDATEคำสั่งด้านบนเราจะเห็นได้อย่างชัดเจนว่าทั้งสองตารางได้รับการอัพเดตโดยคำสั่งการอัพเดทเดียวโดยอาศัยคีย์ต่างประเทศที่กำหนดเป็นON UPDATE CASCADE:

ป้อนคำอธิบายรูปภาพที่นี่ คลิกที่ภาพด้านบนเพื่อดูภาพที่ชัดเจนยิ่งขึ้น

สุดท้ายเราจะล้างตารางชั่วคราวของเรา:

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

1วิธีที่ต้องการในการทำสิ่งนี้โดยใช้กุญแจตัวแทนคือ:

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;

เพื่อความสมบูรณ์แผนสำหรับคำสั่งการอัพเดทนั้นง่ายมากและแสดงข้อดีอย่างหนึ่งของคีย์ตัวแทนนั่นคือมีเพียงแถวเดียวเท่านั้นที่ต้องได้รับการอัปเดตเมื่อเทียบกับทุกแถวที่มีคีย์ในสถานการณ์คีย์ธรรมชาติ:

ป้อนคำอธิบายรูปภาพที่นี่

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

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

เอาต์พุตจากสองSELECTข้อความด้านบนคือ:

ป้อนคำอธิบายรูปภาพที่นี่

โดยพื้นฐานแล้วผลลัพธ์จะอยู่ที่ประมาณเดียวกัน ความแตกต่างที่สำคัญอย่างหนึ่งคือคีย์ธรรมชาติที่กว้างนั้นไม่ได้ทำซ้ำในทุกตารางที่มีคีย์ต่างประเทศเกิดขึ้น ในตัวอย่างของฉันฉันใช้VARCHAR(200)คอลัมน์ที่จะถือชื่อบุคคลซึ่งจำเป็นต้องใช้ทุกที่VARCHAR(200) หากมีแถวจำนวนมากและตารางจำนวนมากที่มีคีย์ต่างประเทศนั่นจะเพิ่มหน่วยความจำที่สูญเปล่าไปมาก หมายเหตุฉันไม่ได้พูดถึงพื้นที่ดิสก์ที่ถูกทำลายเนื่องจากคนส่วนใหญ่พูดว่าพื้นที่ว่างในดิสก์มีราคาถูกจนไม่ต้องเสียค่าใช้จ่าย หน่วยความจำ แต่มีราคาแพงและสมควรที่จะหวงแหน การใช้จำนวนเต็ม 4 ไบต์สำหรับคีย์จะช่วยประหยัดหน่วยความจำจำนวนมากเมื่อคุณพิจารณาความยาวชื่อโดยเฉลี่ยประมาณ 15 ตัวอักษร

สัมผัสกับคำถามเกี่ยวกับวิธีการและเหตุผลที่ปุ่มสามารถเปลี่ยนเป็นคำถามเกี่ยวกับเหตุผลในการเลือกปุ่มธรรมชาติกุญแจตัวแทนซึ่งเป็นที่น่าสนใจและอาจจะเป็นคำถามที่สำคัญมากขึ้นโดยเฉพาะอย่างยิ่งที่ผลการดำเนินงานคือการออกแบบ-เป้าหมาย ดูคำถามของฉันที่นี่เกี่ยวกับที่


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


3
เพื่อหลีกเลี่ยงการเรียงซ้อน (ซึ่งมีปัญหาในบางสถานการณ์) คุณสามารถทำให้คอลัมน์ FK เป็นโมฆะได้ดังนั้นหากคุณต้องการเปลี่ยน PK คุณสามารถอัปเดตแถวที่เกี่ยวข้องเป็น NULL (เป็นกลุ่มถ้ามีจำนวนมากหรือตามตาราง หากมีตารางจำนวนมากหรือทั้งสองอย่าง) จากนั้นเปลี่ยนค่า PK แล้วเปลี่ยน FK อีกครั้ง
Aaron Bertrand

8

ในขณะที่คุณสามารถใช้รหัสที่เป็นธรรมชาติและ / หรือไม่แน่นอนเป็น PK ของคุณในประสบการณ์ของฉันที่นำไปสู่ปัญหาซึ่งมักจะสามารถป้องกันได้โดยการใช้ PK ซึ่งตรงกับเงื่อนไขเหล่านี้:

 Guaranteed Unique, Always Exists, Immutable, and Concise.

ตัวอย่างเช่นหลาย บริษัท ในสหรัฐอเมริกาพยายามใช้หมายเลขประกันสังคมเป็นหมายเลข ID ส่วนบุคคล (และ PKs) ในระบบของพวกเขา จากนั้นพวกเขาพบปัญหาต่อไปนี้ - ข้อผิดพลาดในการป้อนข้อมูลที่นำไปสู่หลายระเบียนที่ต้องได้รับการซ่อมแซมคนที่ไม่มี SSN คนที่รัฐบาลเปลี่ยน SSN คนที่มี SSN ซ้ำกัน

ฉันได้เห็นทุกสถานการณ์ ฉันเคยเห็น บริษัท ที่ไม่ต้องการให้ลูกค้าของพวกเขาเป็น "เพียงแค่ตัวเลข" ซึ่งหมายความว่า PK ของพวกเขาจบลงด้วยการ 'แรก + กลาง + สุดท้าย + DOB + zip' หรือบางเรื่องที่คล้ายกัน ในขณะที่พวกเขาเพิ่มเขตข้อมูลมากพอที่จะรับประกันความไม่เหมือนใครแบบสอบถามของพวกเขาน่ากลัวและการปรับปรุงหนึ่งในเขตข้อมูลเหล่านั้นหมายถึงการไล่ตามปัญหาความสอดคล้องของข้อมูล

จากประสบการณ์ของฉัน PK ที่สร้างขึ้นโดยฐานข้อมูลนั้นมักจะเป็นทางออกที่ดีกว่า

ฉันแนะนำบทความนี้สำหรับคำแนะนำเพิ่มเติม: http://www.agiledata.org/essays/keys.html


6
หนึ่งในคำแนะนำที่ดีจากบทความของ Scott Ambler ที่อ้างถึงในคำตอบของคุณ: "บางคนจะบอกคุณว่าคุณควรใช้กุญแจธรรมชาติและคนอื่น ๆ จะบอกคุณว่าคุณควรใช้กุญแจตัวแทนเสมอคนเหล่านี้มักพิสูจน์ว่าผิด พวกเขากำลังทำอะไรมากกว่าการแบ่งปันอคติของ "data data" ของพวกเขากับคุณความจริงก็คือว่ากุญแจธรรมชาติและปุ่มตัวแทนแต่ละคนมีข้อดีและข้อเสียของพวกเขาและไม่มีกลยุทธ์ที่สมบูรณ์แบบสำหรับทุกสถานการณ์ "
nvogel

7

คีย์หลักสามารถเปลี่ยนแปลงได้เมื่อเกี่ยวข้องกับการซิงโครไนซ์ กรณีนี้อาจเกิดขึ้นเมื่อคุณมีไคลเอ็นต์ที่ถูกตัดการเชื่อมต่อและทำการซิงโครไนซ์ข้อมูลกับเซิร์ฟเวอร์ในบางช่วงเวลา

ไม่กี่ปีที่ผ่านมาฉันทำงานกับระบบที่ข้อมูลเหตุการณ์ทั้งหมดในเครื่องท้องถิ่นมีรหัสแถวเชิงลบเช่น -1, -2 และอื่น ๆ เมื่อข้อมูลถูกซิงโครไนซ์กับเซิร์ฟเวอร์แถวรหัสบนเซิร์ฟเวอร์ถูกนำไปใช้กับ ลูกค้า สมมติว่ารหัสแถวถัดไปบนเซิร์ฟเวอร์คือ 58 จากนั้น -1 จะกลายเป็น 58, -2 59 และอื่น ๆ การเปลี่ยน ID แถวนั้นจะถูกเรียงซ้อนกับเร็กคอร์ด FK ลูกทั้งหมดบนเครื่องโลคัล กลไกนี้ยังใช้เพื่อกำหนดว่าระเบียนใดที่ถูกซิงค์ก่อนหน้านี้

ฉันไม่ได้บอกว่านี่เป็นการออกแบบที่ดี แต่มันเป็นตัวอย่างของคีย์หลักที่เปลี่ยนแปลงตลอดเวลา


5

การออกแบบใด ๆ ที่เกี่ยวข้องกับการเปลี่ยนแปลงPRIMARY KEYเป็นประจำเป็นสูตรสำหรับภัยพิบัติ เหตุผลที่ดีเพียงอย่างเดียวสำหรับการเปลี่ยนแปลงมันคือการรวมกันของสองฐานข้อมูลที่แยกต่างหากก่อนหน้านี้

ในฐานะที่เป็นออกแหลมจากการเปลี่ยนแปลงครั้งคราว @MaxVernon อาจเกิดขึ้น - จากนั้นใช้ON UPDATE CASCADEแม้ว่าส่วนใหญ่ของระบบในปัจจุบันใช้ ID PRIMARY KEYเป็นตัวแทน

ครูสอนอย่างโจเซโกะและเฟเบียนปาสคาล (เว็บไซต์ที่ควรค่าแก่การติดตาม) ไม่เห็นด้วยกับการใช้กุญแจตัวแทน แต่ฉันคิดว่าพวกเขาแพ้การต่อสู้ครั้งนี้


3

ความเสถียรเป็นคุณสมบัติที่ต้องการสำหรับคีย์ แต่เป็นสิ่งที่สัมพันธ์กันและไม่ใช่กฎที่สมบูรณ์ ในทางปฏิบัติมักจะมีประโยชน์ในการเปลี่ยนค่าของคีย์ ในข้อมูลคำศัพท์เชิงสัมพันธ์สามารถระบุได้ด้วยคีย์ (super) เท่านั้น มันตามมาว่าหากมีเพียงหนึ่งคีย์ในตารางที่กำหนดดังนั้นความแตกต่างระหว่าง A) การเปลี่ยนค่าของคีย์หรือ B) แทนที่ชุดของแถวในตารางที่มีชุดของแถวที่คล้ายกันหรือแตกต่างกันซึ่งมีค่าคีย์อื่น ๆ ปัญหาของความหมายมากกว่าตรรกะ

ตัวอย่างที่น่าสนใจมากขึ้นคือกรณีของตารางที่มีหลายคีย์ซึ่งค่าของคีย์เหล่านั้นอย่างน้อยหนึ่งคีย์อาจต้องเปลี่ยนตามความสัมพันธ์กับค่าคีย์อื่น ๆ นำตัวอย่างของตารางพนักงานที่มีสองปุ่ม: ชื่อเข้าสู่ระบบและหมายเลขป้าย นี่คือตัวอย่างแถวจากตารางนั้น:

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

หาก ZoeS แพ้ตราของเธอบางทีเธออาจถูกจัดสรรใหม่และรับหมายเลขตราใหม่:

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

หลังจากนั้นเธออาจตัดสินใจเปลี่ยนชื่อเข้าสู่ระบบของเธอ:

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

ทั้งค่าคีย์เปลี่ยนไป - สัมพันธ์กัน โปรดทราบว่าไม่จำเป็นต้องสร้างความแตกต่างใด ๆ ซึ่งถือว่าเป็น "หลัก"

ในทางปฏิบัติ "การเปลี่ยนแปลงไม่ได้" คือไม่เคยเปลี่ยนค่าแน่นอนไม่สามารถบรรลุได้หรืออย่างน้อยก็เป็นไปไม่ได้ที่จะตรวจสอบ ในกรณีที่การเปลี่ยนแปลงนั้นสร้างความแตกต่างหลักสูตรที่ปลอดภัยที่สุดอาจจะถือว่าคีย์ใด ๆ (หรือคุณลักษณะใด ๆ ) อาจต้องเปลี่ยน


ฉันลดระดับความคิดเห็นของคุณเนื่องจากคำสั่งต่อไปนี้: "ในทางปฏิบัติ" ความไม่สามารถเปลี่ยนแปลงได้ "คือไม่เคยเปลี่ยนค่าอย่างแน่นอนไม่สามารถทำได้หรืออย่างน้อยก็เป็นไปไม่ได้ที่จะยืนยัน" ความไม่สามารถเปลี่ยนได้นั้นเป็นไปได้และเป็นหนึ่งในเหตุผลที่สำคัญที่สุดในการใช้กุญแจตัวแทน
Byron Jones

3
คุณจะรู้ได้อย่างไรว่าบางคนจะไม่เปลี่ยนค่าคีย์ในสัปดาห์หน้าหรือในเวลา 10 ปี คุณอาจคิดว่าพวกเขาจะไม่ได้ แต่คุณไม่สามารถป้องกันสิ่งที่เกิดขึ้นได้ตามความเป็นจริง (ถ้าคุณเป็นผู้รับผิดชอบ แต่เพียงผู้เดียวคุณสามารถวางสิ่งกีดขวางเพื่อให้ทุกคนอยู่ในความเป็นอมตะได้ฉันคิดว่า สิ่งที่สำคัญคือการเปลี่ยนแปลงนั้นเกิดขึ้นไม่บ่อยนักไม่ว่าจะไม่เกิดขึ้น
nvogel

3

น่าสนใจพอคำถามที่ถูกเชื่อมโยงเกี่ยวกับการเรียงลำดับของ ROWGUID นั้นมีกรณีการใช้งานของตนเอง: เมื่อคุณมีคีย์หลักที่ขัดแย้งกันในฐานข้อมูลที่จำเป็นต้องซิงโครไนซ์ หากคุณมีฐานข้อมูลสองฐานที่ต้องปรับยอดและใช้ลำดับสำหรับคีย์หลักคุณจะต้องเปลี่ยนหนึ่งในคีย์เพื่อให้สามารถคงอยู่ได้

ในโลกอุดมคตินี้จะไม่เกิดขึ้น คุณจะใช้ GUID สำหรับคีย์หลักเพื่อเริ่มต้นด้วย แม้ว่าในความเป็นจริงคุณอาจไม่ได้มีฐานข้อมูลแบบกระจายเมื่อคุณเริ่มออกแบบและการแปลงเป็น GUID อาจเป็นความพยายามที่จัดลำดับความสำคัญด้านล่างทำให้มีการกระจายเนื่องจากถือว่ามีผลกระทบสูงกว่าการใช้การอัปเดตคีย์ สิ่งนี้อาจเกิดขึ้นหากคุณมีฐานรหัสขนาดใหญ่ที่ขึ้นอยู่กับคีย์จำนวนเต็มและต้องการการแก้ไขครั้งใหญ่เพื่อแปลงเป็น GUID นอกจากนี้ยังมีความจริงที่ว่า GUID แบบกระจัดกระจาย (GUID ที่ไม่ใกล้เคียงกันซึ่งเกิดขึ้นหากคุณสร้างแบบสุ่มตามที่ควร) อาจทำให้เกิดปัญหาสำหรับดัชนีบางประเภทได้เช่นกันซึ่งหมายความว่าคุณต้องการหลีกเลี่ยงการใช้ เป็นปุ่มหลัก (กล่าวถึงโดยByron Jones )


0

สถานการณ์ที่เป็นไปได้อย่างหนึ่งคือสมมติว่าคุณมี บริษัท ในเครือที่มีรหัสเฉพาะและคุณรู้ว่าพวกเขาจะไม่ซ้ำกันในเครือเนื่องจากพวกเขามีอักขระเริ่มต้นที่ไม่ซ้ำกัน บริษัท ในเครือโหลดข้อมูลไปยังตารางต้นแบบ มีการประมวลผลบันทึกแล้วกำหนด ID หลัก ผู้ใช้จำเป็นต้องเข้าถึงข้อมูลทันทีที่มีการโหลดแม้ว่าพวกเขาจะยังไม่ได้ดำเนินการ คุณต้องการให้ ID หลักเป็นไปตามการดำเนินการตามคำสั่งและคุณจะไม่ดำเนินการตามลำดับที่มีการโหลดเรคคอร์ด ฉันรู้ว่าประดิษฐ์ขึ้นเล็กน้อย


-1

ลองนึกภาพสถานการณ์ที่เกิดขึ้นเมื่อมีคนเลือก National Insurance Number (NIN) เป็นคีย์หลักและผู้ดำเนินการแทรกแถวด้วย NIN ที่ไม่ถูกต้อง หลังจากแทรกค่ามีสองวิธีในการแก้ไขข้อผิดพลาด:

  1. ลบบันทึกที่ผิดพลาดและใส่ใหม่
  2. อัพเดตค่าเป็นค่าที่ถูกต้องและใช้On Update Cascadeหากมีข้อ จำกัด ของ Referential Integrity ในคอลัมน์นั้น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.