Tại sao giá trị khóa chính sẽ thay đổi?


18

Tôi đã nghiên cứu về khái niệm ROWGUID gần đây và tình cờ thấy câu hỏi này . Câu trả lời này đã đưa ra cái nhìn sâu sắc, nhưng đã đưa tôi xuống một lỗ thỏ khác với đề cập đến việc thay đổi giá trị khóa chính.

Sự hiểu biết của tôi luôn luôn là một khóa chính nên không thay đổi và việc tìm kiếm của tôi kể từ khi đọc câu trả lời này chỉ cung cấp câu trả lời phản ánh giống như một cách thực hành tốt nhất.

Trong trường hợp nào, giá trị khóa chính sẽ cần được thay đổi sau khi bản ghi được tạo?


7
Khi một khóa chính được chọn mà không phải là bất biến?
ypercubeᵀᴹ

2
Chỉ là một nit nhỏ cho tất cả các câu trả lời dưới đây cho đến nay. Thay đổi một giá trị trong khóa chính không phải là vấn đề lớn trừ khi khóa chính cũng xảy ra là chỉ mục được nhóm. Nó chỉ thực sự quan trọng nếu các giá trị của chỉ số cụm thay đổi.
Kenneth Fisher

6
@KennethFisher hoặc nếu nó được tham chiếu bởi một (hoặc nhiều) FK trong bảng khác hoặc cùng bảng và một thay đổi phải được xếp thành nhiều hàng, (có thể hàng triệu hoặc hàng tỷ) hàng.
ypercubeᵀᴹ

9
Hỏi Skype. Khi tôi đăng ký vài năm trước, tôi đã nhập tên người dùng không chính xác (để lại một chữ cái trong tên cuối cùng của tôi). Tôi đã cố gắng sửa nó nhiều lần, nhưng họ không thể thay đổi vì nó được sử dụng cho khóa chính và họ không hỗ trợ thay đổi nó. Đó là một trường hợp khách hàng muốn khóa chính thay đổi, nhưng Skype đã không xảy ra để hỗ trợ điều đó. Họ có thể hỗ trợ sự thay đổi đó nếu họ muốn (hoặc họ có thể tạo ra một thiết kế tốt hơn), nhưng hiện tại không có gì để cho phép điều đó. Vì vậy, tên người dùng của tôi vẫn không chính xác.
Aaron Bertrand

3
Tất cả các giá trị trong thế giới thực có thể thay đổi (vì nhiều nguyên nhân). Đây là một trong những động lực ban đầu cho các khóa thay thế / tổng hợp: để có thể tạo ra các giá trị nhân tạo có thể dựa vào để không bao giờ thay đổi.
RBarryYoung

Câu trả lời:


24

Nếu bạn đang sử dụng tên của một người làm khóa chính và tên của họ đã thay đổi, bạn sẽ cần thay đổi khóa chính. Đây là những gì ON UPDATE CASCADEđược sử dụng vì về cơ bản nó xếp tầng thay đổi xuống tất cả các bảng có liên quan có mối quan hệ khóa ngoài với khóa chính.

Ví dụ:

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 SELECTso với cả hai bảng:

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

Trả về:

nhập mô tả hình ảnh ở đây

Nếu chúng tôi cập nhật PersonKeycột và chạy lại 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;

chúng tôi thấy:

nhập mô tả hình ảnh ở đây

Nhìn vào kế hoạch cho UPDATEtuyên bố trên , chúng ta thấy rõ cả hai bảng được cập nhật bởi một tuyên bố cập nhật duy nhất nhờ khóa ngoại được xác định là ON UPDATE CASCADE:

nhập mô tả hình ảnh ở đây nhấp vào hình ảnh trên để thấy rõ hơn

Cuối cùng, chúng tôi sẽ dọn sạch các bảng tạm thời của chúng tôi:

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

Cách 1 ưu tiên để thực hiện việc này bằng các phím thay thế sẽ là:

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;

Để đầy đủ, kế hoạch cho câu lệnh cập nhật rất đơn giản và cho thấy một lợi thế để thay thế các khóa, cụ thể là chỉ một hàng duy nhất cần được cập nhật trái ngược với mọi hàng có chứa khóa trong kịch bản khóa tự nhiên:

nhập mô tả hình ảnh ở đây

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

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

Đầu ra từ hai SELECTcâu lệnh trên là:

nhập mô tả hình ảnh ở đây

Về cơ bản, kết quả là gần như nhau. Một điểm khác biệt chính là khóa tự nhiên rộng không được lặp lại trong mỗi bảng nơi xảy ra khóa ngoại. Trong ví dụ của tôi, tôi đang sử dụng một VARCHAR(200)cột để giữ tên của người đó, điều này bắt buộc phải sử dụng VARCHAR(200) ở mọi nơi . Nếu có rất nhiều hàng và rất nhiều bảng chứa khóa ngoại, điều đó sẽ làm tăng thêm rất nhiều bộ nhớ lãng phí. Lưu ý, tôi không nói về việc dung lượng ổ đĩa bị lãng phí vì hầu hết mọi người nói rằng dung lượng ổ đĩa rẻ đến mức về cơ bản là miễn phí. Bộ nhớ, tuy nhiên, đắt tiền và xứng đáng được trân trọng. Sử dụng số nguyên 4 byte cho khóa sẽ tiết kiệm một lượng lớn bộ nhớ khi bạn xem xét độ dài tên trung bình khoảng 15 ký tự.

Tiếp theo câu hỏi về cách thứclý do tại sao các phím có thể thay đổi là câu hỏi về lý do tại sao chọn khóa tự nhiên thay vì khóa thay thế, đây là một câu hỏi thú vị và có lẽ quan trọng hơn, đặc biệt là hiệu suất là mục tiêu thiết kế. Xem câu hỏi của tôi ở đây về điều đó.


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


3
Để tránh CASCADE (có vấn đề trong một số trường hợp nhất định), bạn cũng có thể biến các cột FK thành null, vì vậy nếu bạn cần thay đổi PK, bạn có thể cập nhật các hàng liên quan thành NULL (trong các đoạn, nếu có nhiều hoặc theo bảng , nếu có nhiều bảng hoặc cả hai), sau đó thay đổi giá trị PK, sau đó thay đổi lại các FK.
Aaron Bertrand

8

Mặc dù bạn có thể sử dụng một khóa tự nhiên và / hoặc có thể thay đổi như PK của bạn, nhưng theo kinh nghiệm của tôi dẫn đến các vấn đề, thường có thể được ngăn chặn bằng cách sử dụng PK đáp ứng các điều kiện sau:

 Guaranteed Unique, Always Exists, Immutable, and Concise.

Ví dụ: nhiều công ty ở Hoa Kỳ cố gắng sử dụng Số An sinh Xã hội làm số ID cá nhân, (và PK) trong hệ thống của họ. Sau đó, họ gặp phải các vấn đề sau - lỗi nhập dữ liệu dẫn đến nhiều bản ghi phải sửa chữa, những người không có SSN, những người có SSN bị chính phủ thay đổi, những người có SSN trùng lặp.

Tôi đã từng thấy một trong những kịch bản đó. Tôi cũng đã thấy các công ty không muốn khách hàng của họ "chỉ là một con số", điều đó có nghĩa là PK của họ cuối cùng là 'đầu tiên + giữa + cuối + DOB + zip' hoặc một số điều vô nghĩa tương tự. Mặc dù họ đã thêm đủ các trường để gần như đảm bảo tính duy nhất, nhưng các truy vấn của họ rất tệ và cập nhật bất kỳ một trong các trường đó có nghĩa là theo đuổi các vấn đề về tính nhất quán của dữ liệu.

Theo kinh nghiệm của tôi, một PK được tạo bởi chính cơ sở dữ liệu hầu như luôn là một giải pháp tốt hơn.

Tôi đề xuất bài viết này cho các gợi ý bổ sung: http://www.agiledata.org/essays/keys.html


6
Một lời khuyên hữu ích từ bài viết của Scott Ambler được tham chiếu trong câu trả lời của bạn: "Một số người sẽ nói với bạn rằng bạn nên luôn luôn sử dụng các khóa tự nhiên và những người khác sẽ nói với bạn rằng bạn nên luôn luôn sử dụng các khóa thay thế. họ đang làm ít hơn là chia sẻ định kiến ​​về "tôn giáo dữ liệu" của họ với bạn. Thực tế là mỗi khóa tự nhiên và thay thế đều có ưu điểm và nhược điểm, và không có chiến lược nào là hoàn hảo cho mọi tình huống. "
nvogel

7

Khóa chính có thể được thay đổi khi liên quan đến đồng bộ hóa. Đây có thể là trường hợp khi bạn có một máy khách bị ngắt kết nối và nó đồng bộ hóa dữ liệu với máy chủ theo các khoảng thời gian nhất định.

Vài năm trước tôi đã làm việc trên một hệ thống trong đó tất cả dữ liệu sự kiện trên máy cục bộ có Id hàng âm, như -1, -2, v.v. Khi dữ liệu được đồng bộ hóa với máy chủ, Id hàng trên máy chủ được áp dụng cho khách hàng Giả sử Id hàng tiếp theo trên máy chủ là 58. Sau đó -1 sẽ trở thành 58, -2 59 trở đi. Thay đổi ID hàng đó sẽ được xếp thành tất cả các bản ghi FK con trên máy cục bộ. Cơ chế này cũng được sử dụng để xác định bản ghi nào đã được đồng bộ hóa trước đó.

Tôi không nói rằng đây là một thiết kế tốt, nhưng nó là một ví dụ về khóa chính thay đổi theo thời gian.


5

Bất kỳ thiết kế nào liên quan đến việc thay đổi PRIMARY KEYmột cách thường xuyên là một công thức cho thảm họa. Lý do chính đáng duy nhất để thay đổi nó là sự hợp nhất của hai cơ sở dữ liệu riêng biệt trước đây.

Như @MaxVernon đã chỉ ra những thay đổi không thường xuyên có thể xảy ra - sau đó sử dụng ON UPDATE CASCADE, mặc dù phần lớn các hệ thống hiện nay sử dụng ID làm vật thay thế PRIMARY KEY.

Những người theo chủ nghĩa thuần túy như Joe CelkoFabian Pascal (một trang đáng theo dõi) không đồng ý với việc sử dụng khóa thay thế, nhưng tôi nghĩ rằng họ đã thua trận chiến đặc biệt này.


3

Sự ổn định là một tài sản mong muốn cho một khóa nhưng nó là một điều tương đối và không phải là một quy tắc tuyệt đối. Trong thực tế, việc thay đổi các giá trị của khóa thường rất hữu ích. Trong thuật ngữ quan hệ, dữ liệu chỉ có thể được xác định bằng các khóa (siêu) của nó. Theo sau, nếu chỉ có một khóa trong một bảng nhất định thì sự khác biệt giữa A) thay đổi một giá trị khóa hoặc B) thay thế tập hợp các hàng trong một bảng bằng một số hàng tương tự hoặc khác nhau chứa các giá trị khóa khác, về cơ bản là một vấn đề về ngữ nghĩa hơn là logic.

Một ví dụ thú vị hơn là trường hợp một bảng có nhiều khóa trong đó các giá trị của một hoặc nhiều khóa đó có thể phải thay đổi liên quan đến các giá trị khóa khác. Lấy ví dụ về bảng Nhân viên có hai khóa: Tên đăng nhập và Số hiệu. Đây là một hàng mẫu từ bảng đó:

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

Nếu ZoeS mất huy hiệu thì có thể cô ấy được cấp một cái mới và nhận được số huy hiệu mới:

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

Sau đó, cô ấy có thể quyết định thay đổi tên đăng nhập của mình:

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

Cả hai giá trị chính đã thay đổi - liên quan đến nhau. Lưu ý rằng nó không nhất thiết phải tạo ra bất kỳ sự khác biệt nào mà cái được coi là "chính".

Trong thực tế "bất biến", tức là tuyệt đối không bao giờ thay đổi giá trị, là không thể thực hiện được hoặc ít nhất là không thể xác minh. Trong phạm vi mà sự thay đổi tạo ra sự khác biệt, khóa học an toàn nhất có lẽ là giả định rằng bất kỳ khóa nào (hoặc bất kỳ thuộc tính nào) có thể cần phải thay đổi.


Tôi đã đánh giá thấp nhận xét của bạn do tuyên bố sau: "Trong thực tế" tính bất biến ", tức là tuyệt đối không bao giờ thay đổi giá trị, không thể thực hiện được hoặc ít nhất là không thể xác minh." Tính bất biến là có thể, và là một trong những lý do quan trọng nhất để sử dụng khóa thay thế.
Byron Jones

3
Làm thế nào bạn có thể biết rằng ai đó sẽ không thay đổi giá trị chính vào tuần tới hoặc sau 10 năm nữa? Bạn có thể cho rằng họ sẽ không nhưng thực tế bạn không thể ngăn chặn điều đó xảy ra (nếu bạn chịu trách nhiệm thì bạn có thể đưa ra các rào cản để ngăn chặn mọi người khác liên tục, tôi cho rằng đó có vẻ như là một trường hợp cạnh tranh). Điều thực sự quan trọng là những thay đổi rất không thường xuyên, không phải là chúng sẽ không xảy ra.
nvogel

3

Thật thú vị, câu hỏi được liên kết về loại ROWGUID cung cấp trường hợp sử dụng riêng của nó: khi bạn có các khóa chính xung đột trong cơ sở dữ liệu cần được đồng bộ hóa. Nếu bạn có hai cơ sở dữ liệu cần được đối chiếu và chúng sử dụng trình tự cho các khóa chính, bạn sẽ muốn một trong các khóa thay đổi để nó có thể duy nhất.

Trong một thế giới lý tưởng, điều này sẽ không bao giờ xảy ra. Bạn sẽ sử dụng GUID cho các khóa chính để bắt đầu. Tuy nhiên, trên thực tế, bạn thậm chí có thể không có cơ sở dữ liệu phân tán khi bắt đầu thiết kế và chuyển đổi nó thành GUID có thể là một nỗ lực được ưu tiên dưới đây khiến nó được phân phối vì nó được coi là có tác động cao hơn so với việc thực hiện cập nhật khóa. Điều này có thể xảy ra nếu bạn có một cơ sở mã lớn phụ thuộc vào các khóa số nguyên và sẽ yêu cầu sửa đổi lớn để chuyển đổi thành GUID. Ngoài ra còn có một thực tế là các GUID thưa thớt (GUID không gần nhau, xảy ra nếu bạn tạo chúng ngẫu nhiên như bạn có thể) có thể gây ra sự cố cho một số loại chỉ mục nhất định, điều đó có nghĩa là bạn muốn tránh sử dụng chúng là khóa chính (được đề cập bởi Byron Jones ).


0

Một tình huống có thể xảy ra là giả sử bạn có các chi nhánh có ID duy nhất và bạn biết họ sẽ không trùng lặp giữa các chi nhánh vì họ có ký tự bắt đầu duy nhất. Các chi nhánh tải dữ liệu vào một bảng tổng thể. Có hồ sơ được xử lý và sau đó được gán một ID chính. Người dùng cần truy cập vào các bản ghi ngay khi chúng được tải ngay cả khi chúng chưa được xử lý. Bạn muốn ID chính dựa trên đơn hàng được xử lý và bạn sẽ không luôn xử lý theo thứ tự các bản ghi đã được tải. Tôi biết một chút bịa đặt.


-1

Hãy tưởng tượng một tình huống như khi ai đó chọn Số Bảo hiểm Quốc gia (NIN) làm Khóa chính và bằng cách nào đó, một nhà điều hành chèn một hàng với NIN sai. Sau khi chèn giá trị, có hai cách để sửa lỗi:

  1. Xóa bản ghi nhầm và chèn một bản ghi mới
  2. Cập nhật giá trị thành giá trị chính xác và sử dụng On Update Cascade nếu có ràng buộc toàn vẹn tham chiếu trên cột đó
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.