Cột NVARCHAR là cột CHÍNH HÃNG hoặc là cột ĐỘC ĐÁO


11

Tôi đang phát triển cơ sở dữ liệu SQL Server 2012 và tôi nghi ngờ về các cột nvarchar làm khóa chính.

Tôi có bảng này:

CREATE TABLE [dbo].[CODES]
(
    [ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [FLAG] [tinyint] NOT NULL,
    [IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
     CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE_LEVEL] ASC,
        [CODE] ASC
    )
)

Nhưng bây giờ tôi muốn sử dụng [CODE]cột làm khóa chính và loại bỏ [ID_CODE]cột.

Có bất kỳ vấn đề hoặc hình phạt nếu tôi có một NVARCHARcột như PRIMARY KEY?

[CODE]giá trị cột phải là duy nhất, vì vậy tôi đã nghĩ rằng tôi có thể đặt UNIQUEràng buộc cho cột đó.

Tôi có phải sử dụng [CODE]làm khóa chính hay tốt hơn nếu tôi đặt UNIQUEràng buộc trên [CODE]cột?


1
Điều quan trọng cần xem xét là có bao nhiêu hàng trong bảng của bạn?
James Z

Đây không phải là một câu trả lời cho mỗi gia nhập , nhưng tôi nghiêng khi nghĩ rằng bạn CODEcột phải là duy nhất, nhưng không phải là một Primary Key. Tôi nghi ngờ rằng nó mang thông tin. Nếu thông tin đó có thể thay đổi theo bất kỳ cách nào, thì bạn CODEnên thay đổi hoặc hết hạn. Điều đó sẽ làm cho Khóa chính của bạn biến động và tôi không thể thấy kết thúc tốt đẹp đó. Tốt nhất để PK của bạn chỉ là một chìa khóa và CODE của bạn có thể làm những gì nó thích. Chỉ là một ý kiến.
Manngo 18/03/18

@Manngo, cảm ơn bình luận của bạn. Có, tôi đã làm theo cách đó: ID_CODE là khóa chính và CODE là ĐỘC ĐÁO.
VansFannel

Câu trả lời:


13

Có, hoàn toàn có những hậu quả tiêu cực đối với việc sử dụng một chuỗi thay vì kiểu số cho Khóa chính và thậm chí còn nhiều hơn nếu PK đó được phân cụm (mà thực sự là trong trường hợp của bạn). Tuy nhiên, mức độ bạn thấy (các) hiệu ứng của việc sử dụng trường chuỗi là một hàm của a) có bao nhiêu hàng trong bảng này và b) có bao nhiêu hàng trong các bảng khác được Khóa ngoài cho PK này. Nếu bạn chỉ có 10k hàng trong bảng này và 100k hàng trong một vài bảng khác FK đến bảng này qua trường đó, thì có lẽ nó sẽ không được chú ý. Nhưng những hiệu ứng đó chắc chắn trở nên đáng chú ý hơn khi số lượng hàng tăng lên.

Bạn cần xem xét rằng các trường trong Chỉ mục được nhóm được chuyển sang Chỉ mục không được nhóm. Vì vậy, bạn không chỉ nhìn vào tối đa 40 byte mỗi hàng, mà là (40 * some_number) byte. Và trong bất kỳ bảng FK nào, bạn có cùng 40 byte trong hàng cộng với thường xuyên hơn sẽ không có chỉ mục Không phân cụm trên trường đó vì nó đang được sử dụng trong THAM GIA, vì vậy giờ đây nó thực sự được nhân đôi trong bất kỳ bảng nào mà FK cái này. Nếu một người có xu hướng nghĩ rằng 40 byte * 1 triệu hàng * 10 bản sao thì không có gì phải lo lắng, vui lòng xem bài viết của tôi Disk Is Cheap! ORLY? trong đó nêu chi tiết tất cả (hoặc ít nhất là hầu hết) các khu vực bị ảnh hưởng bởi quyết định này.

Một điều khác cần xem xét là việc lọc và sắp xếp các chuỗi, đặc biệt là khi không sử dụng Collation nhị phân (tôi giả sử bạn đang sử dụng mặc định cơ sở dữ liệu thường không phân biệt chữ hoa chữ thường) sẽ kém hiệu quả hơn (tức là mất nhiều thời gian hơn) so với khi sử dụng INT/ BIGINT. Điều này tác động đến tất cả các truy vấn lọc / tham gia / sắp xếp trên trường này.

Do đó, sử dụng một cái gì đó như thế CHAR(5)có lẽ sẽ ổn cho PK cụm, nhưng chủ yếu là nếu nó cũng được xác định bằng COLLATE Latin1_General_100_BIN2(hoặc một cái gì đó tương tự).

Và giá trị của [CODE]bao giờ có thể thay đổi? Nếu có thì đó là lý do thậm chí nhiều hơn để không sử dụng nó như một PK (ngay cả khi bạn đặt FK thành ON UPDATE CASCADE). Nếu nó không thể hoặc sẽ không bao giờ thay đổi thì tốt, nhưng vẫn còn quá nhiều lý do để không sử dụng nó làm PK cụm.

Tất nhiên, câu hỏi có thể được đặt ra không chính xác vì có vẻ như bạn hiện đã có trường này trong PK của mình.

Bất kể, tùy chọn tốt nhất của bạn, cho đến nay, là sử dụng [ID_CODE]như PK cụm, sử dụng trường đó trong các bảng có liên quan làm FK và giữ [CODE]dưới dạng UNIQUE INDEX(có nghĩa là "khóa thay thế").


Cập nhật thêm
một chút thông tin dựa trên câu hỏi này trong một bình luận về câu trả lời này:

Có phải [ID_CODE], là PRIMARY KEY, tùy chọn tốt nhất nếu tôi sử dụng cột [CODE] để tra cứu bảng không?

Tất cả điều này phụ thuộc vào rất nhiều yếu tố, một số trong đó tôi đã đề cập nhưng sẽ trình bày lại:

Khóa chính là cách xác định hàng riêng lẻ, cho dù nó có được tham chiếu bởi bất kỳ Khóa ngoại nào hay không. Cách hệ thống của bạn xác định nội bộ hàng có liên quan đến, nhưng không nhất thiết giống như cách người dùng của bạn xác định hàng / hàng đó. Bất kỳ cột KHÔNG NULL nào có dữ liệu duy nhất đều có thể hoạt động, nhưng có những vấn đề thực tế cần xem xét, đặc biệt là nếu trên thực tế, PK được tham chiếu bởi bất kỳ FK nào. Ví dụ, GUID là duy nhất và một số người thực sự thích sử dụng chúng vì nhiều lý do, nhưng chúng khá tệ đối với Chỉ mục cụm ( NEWSEQUENTIALIDtốt hơn, nhưng không hoàn hảo). Mặt khác, GUID chỉ hoạt động tốt như các phím thay thế và được ứng dụng sử dụng để tra cứu hàng, nhưng THAM GIA vẫn được thực hiện bằng cách sử dụng PK INT (hoặc tương tự).

Cho đến nay bạn chưa nói với chúng tôi cách [CODE]trường phù hợp với hệ thống từ mọi góc độ, ngoài bây giờ đề cập rằng đây là cách bạn tìm kiếm các hàng, nhưng đó có phải là cho tất cả các truy vấn hay chỉ một số? Vì thế:

  • Về [CODE]giá trị:

    • Nó được tạo ra như thế nào?
    • Là tăng dần hay psuedo-ngẫu nhiên?
    • Là chiều dài đồng đều hoặc chiều dài khác nhau?
    • Những nhân vật được sử dụng?
    • Nếu sử dụng các ký tự chữ cái: nó là trường hợp nhạy cảm hoặc không nhạy cảm?
    • Nó có thể thay đổi sau khi được chèn không?
  • Về bảng này:

    • Có bảng nào khác FK cho bảng này không? Hoặc các trường này ( [CODE]hoặc [ID_CODE]) được sử dụng trong các bảng khác, ngay cả khi không rõ ràng là Khóa ngoài?
    • Nếu [CODE] là trường duy nhất được sử dụng để có được các hàng riêng lẻ thì [ID_CODE]trường phục vụ cho mục đích gì? Nếu nó không được sử dụng, tại sao lại có nó ở vị trí đầu tiên (có thể phụ thuộc vào câu trả lời cho "Trường có thể [CODE]thay đổi không?")?
    • Có bao nhiêu hàng trong bảng này?
    • Nếu các bảng khác để tham chiếu bảng này, có bao nhiêu và bao nhiêu hàng trong mỗi bảng?
    • Các chỉ số cho bảng này là gì?

Quyết định này không thể được đưa ra hoàn toàn cho câu hỏi "NVARCHAR có hay không?". Tôi một lần nữa sẽ nói rằng nói chung tôi không thấy đó là một ý tưởng tốt, nhưng chắc chắn sẽ có lúc nó ổn. Với rất ít trường trong bảng này, không có khả năng có thêm hoặc ít nhất là không có nhiều chỉ mục. Vì vậy, bạn có thể ổn cả hai cách để có [CODE]được Chỉ số cụm. Và nếu không có bảng nào khác tham chiếu bảng này thì bạn cũng có thể biến nó thành PK. Nhưng, nếu các bảng khác tham chiếu bảng này thì tôi sẽ chọn [ID_CODE]trường là PK, ngay cả khi Không phân cụm.


Liệu downvoter ẩn danh (người dường như cũng đã bình chọn xuống câu trả lời của @noIDonthissystem) sẽ đưa ra bất kỳ lời phê bình mang tính xây dựng nào hoặc chỉ ra một số logic thiếu sót?
Solomon Rutzky

Cảm ơn câu trả lời của bạn. Là [ID_CODE], như PRIMARY KEY, tùy chọn tốt nhất nếu tôi sử dụng [CODE]cột để tra cứu bảng?
VansFannel

@VansFannel vui lòng xem cập nhật của tôi. cảm ơn.
Solomon Rutzky

Tôi đã tham gia cộng đồng dba này để nâng cao câu trả lời này.
Ahmet Arslan

6

Bạn phải tách các khái niệm:

  • khóa chính là một khái niệm thiết kế , một thuộc tính logic của các mục trong bảng. Nó sẽ không thay đổi trong suốt vòng đời của mục nhập bảng và phải là khóa được sử dụng trong ứng dụng để tham chiếu mục nhập.

  • chỉ số cụm là một khái niệm lưu trữ , một thuộc tính vật lý. Nó phải là đường dẫn truy cập phổ biến nhất cho các truy vấn, nó sẽ phục vụ để đáp ứng chỉ số bao trùm cho hầu hết các trường hợp và đáp ứng càng nhiều truy vấn phạm vi càng tốt.

Không yêu cầu khóa chính là chỉ mục được nhóm. Bạn có thể có ID_CODEPK và (CODE_LEVEL, CODE)khóa cụm. Hoặc cách khác xung quanh.

Khóa cụm lớn hơn có một số tác động tiêu cực, vì khóa rộng hơn có nghĩa là mật độ thấp hơn trên các trang chỉ mục và kích thước lớn hơn được tiêu thụ trên tất cả các chỉ mục không được phân cụm. đã có hàng tấn mực tràn về chủ đề này, vd. bắt đầu từ những cân nhắc khác cho khóa phân cụm - cuộc tranh luận về chỉ số phân cụm vẫn tiếp tục! .

Nhưng ý chính của vấn đề là việc lựa chọn khóa chỉ số phân cụm chủ yếu là sự đánh đổi. Một mặt, bạn có các yêu cầu về kích thước lưu trữ, với hiệu quả chung về hiệu suất (khóa lớn hơn -> kích thước lớn hơn -> nhiều IO hơn và băng thông IO có lẽ tài nguyên khan hiếm nhất bạn có). Mặt khác, việc chọn khóa cụm sai trong tên của tiết kiệm không gian có thể có hậu quả về hiệu năng truy vấn, thường tệ hơn các vấn đề phát sinh từ một khóa rộng.

Đối với lựa chọn khóa chính, nó thậm chí không phải là một vấn đề: mô hình dữ liệu của bạn, logic ứng dụng của bạn, sẽ cho biết khóa chính là gì.

Điều đó đang được nói, tôi 2c: NVARCHAR(20)không rộng. Là một kích thước khóa cụm hoàn toàn chấp nhận được, ngay cả đối với một bảng lớn.


Cảm ơn câu trả lời của bạn. Là [ID_CODE], như PRIMARY KEY, tùy chọn tốt nhất nếu tôi sử dụng [CODE]cột (và có thể [CODE_LEVEL]) để tra cứu bảng?
VansFannel

@VansFannel chỉ bạn mới có thể trả lời.
Remus Rusanu

Nhưng theo ý kiến ​​của bạn ...
VansFannel

2
Ý kiến ​​của tôi sẽ phải xem xét DDL chính xác của toàn bộ bảng và tất cả các chỉ mục, khóa ngoại tham chiếu nó, số lượng hàng ước tính, khối lượng công việc truy vấn dự kiến, SLA dự kiến ​​ứng dụng và không phải là ít nhất có sẵn cho phần cứng và cấp phép.
Remus Rusanu

Cảm ơn. Tôi sẽ sử dụng [CODE]cột làm KHÓA CHÍNH.
VansFannel

4

Tôi sẽ không bao giờ cho phép bất cứ ai biến nvarchar(20)thành một PK trong cơ sở dữ liệu của tôi. Bạn lãng phí không gian đĩa và bộ nhớ cache. Mỗi chỉ mục trên bảng này và tất cả các FK để sao chép giá trị rộng này. Có lẽ một char (20) nếu họ có thể biện minh cho nó. Những loại dữ liệu bạn đang cố gắng lưu trữ trong CODE? Bạn có thực sự cần lưu trữ các ký tự nvarchar? Tôi có xu hướng làm cho các giá trị PK "bên trong" không được người dùng nhìn thấy và tôi cố gắng giữ các giá trị được hiển thị riêng biệt. Các giá trị được hiển thị đôi khi cần thay đổi, điều này trở nên rất khó khăn với PK + FK.

Ngoài ra, bạn có nhận ra rằng một 'danh tính bigint (1,1)' có thể tăng lên tới 9.223.372.036.854.775.807 không?

[ID_CODE] [bigint] IDENTITY(1,1)

Trừ khi bạn đang xây dựng cơ sở dữ liệu này cho Google, sẽ không bình thường int identity (1,1)với giới hạn trên 2 tỷ của nó là đủ?


int là 4 byte trong SQL, cung cấp cho bạn -2.1Billion đến + 2.1Billion.
datagod 10/2/2015

@datagod, ha cảm ơn, rất nhiều chữ số tôi đếm sai!
không có ID trên hệ thống này

Cảm ơn câu trả lời của bạn. Là [ID_CODE], như PRIMARY KEY, tùy chọn tốt nhất nếu tôi sử dụng [CODE]cột để tra cứu bảng? Cảm ơn.
VansFannel

Tôi đã từng ở trên chiếc thuyền này cho đến khi tôi có ai đó sử dụng tính chất tuần tự của "int" để dự đoán dữ liệu / người dùng trong DB của tôi và thu hoạch hầu hết mọi thứ tôi có. Không bao giờ lặp lại. Đối mặt với DB cần phải khó khăn hơn một chút để lấy thông tin ra.
DaBlue

3

Không nên có hình phạt cố hữu / đáng chú ý nào ngoài việc bạn có nguy cơ sử dụng các phím rộng khi sử dụng nvarchar / varchar nếu không biết. Đặc biệt nếu bạn bắt đầu kết hợp chúng trong các phím tổng hợp.

Nhưng trong ví dụ của bạn về độ dài (20), bạn sẽ ổn và tôi sẽ không lo lắng nhiều về điều đó. Bởi vì nếu CODE là cách bạn chủ yếu truy vấn dữ liệu của mình - một chỉ mục được nhóm trên đó nghe có vẻ rất hợp lý.

Tuy nhiên, bạn nên xem xét liệu bạn thực sự muốn nó là khóa chính hay chỉ là một chỉ mục (cụm) duy nhất. Có một sự khác biệt (nhỏ) giữa chỉ mục được nhóm và khóa chính (về cơ bản - khóa chính xác định dữ liệu của bạn, nhưng chỉ mục là cách bạn truy vấn dữ liệu), vì vậy nếu bạn muốn bạn có thể dễ dàng biến ID_Code của mình thành khóa chính và tạo một chỉ mục cụm duy nhất trên CODE. (lưu ý: SQL Server sẽ tự động biến Khóa chính của bạn thành một chỉ mục được nhóm, trừ khi bạn tự tạo chỉ mục được phân cụm)

Ngoài ra, hãy xem xét liệu bạn có thực sự cần ID_Code bây giờ bạn có CODE duy nhất không.


2
Trên thực tế, NVARCHAR(20)có kích thước 40 byte (tối đa) và vì là cột có độ dài thay đổi , nên nó không thực sự là lựa chọn tốt nhất cho một chỉ mục được nhóm. ID_CODElà một BIGINT IDENTITYsẽ là tốt hơn nhiều lựa chọn ở đây!
marc_s

Tôi biết đó là 40 byte, nhưng không có nhiều lý do để chỉ định nó, vì nó không ở đâu gần 900 byte. Và nếu bạn chủ yếu truy vấn dữ liệu từ CODE, thì đó sẽ là lựa chọn tốt hơn để tránh duy trì các chỉ mục dư thừa, bởi vì bạn vẫn cần một chỉ mục trên đó, và sau đó bạn phải tìm kiếm thông qua cụm sau
Allan S. Hansen

Đáng nói - điều mà tôi quên đề cập đến và điều mà tôi nghi ngờ là nơi @marc_s đang giải quyết là một chỉ số thuộc loại này có thể dẫn đến sự phân mảnh chỉ số lớn hơn một danh tính tuần tự, nhưng tôi vẫn xem đó là một chỉ số hợp lý trong tình huống cụ thể này về yếu tố truy vấn.
Allan S. Hansen
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.