คอลัมน์ NVARCHAR เป็นคีย์หลักหรือคอลัมน์ที่ไม่ซ้ำกัน


11

ฉันกำลังพัฒนาฐานข้อมูล SQL Server 2012 และฉันมีข้อสงสัยเกี่ยวกับคอลัมน์ nvarchar เป็นคีย์หลัก

ฉันมีตารางนี้:

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

แต่ตอนนี้ฉันต้องการใช้[CODE]คอลัมน์เป็นคีย์หลักและลบ[ID_CODE]คอลัมน์

มีปัญหาหรือบทลงโทษหรือไม่หากฉันมีNVARCHARคอลัมน์เป็นPRIMARY KEY?

[CODE]ค่าของคอลัมน์ต้องไม่ซ้ำกันดังนั้นฉันคิดว่าฉันสามารถตั้งUNIQUEข้อ จำกัด ให้กับคอลัมน์นั้นได้

ฉันต้องใช้[CODE]เป็นคีย์หลักหรือไม่ดีกว่าถ้าฉันตั้งUNIQUEข้อ จำกัด ใน[CODE]คอลัมน์?


1
สิ่งสำคัญในการพิจารณาคือมีกี่แถวในตารางของคุณ?
James Z

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

@ Mango ขอบคุณสำหรับความคิดเห็นของคุณ ใช่ฉันทำเช่นนั้น: ID_CODE เป็นคีย์หลักและ CODE เป็น UNIQUE
VansFannel

คำตอบ:


13

ใช่มีผลกระทบเชิงลบอย่างแน่นอนสำหรับการใช้สตริงแทนที่จะเป็นชนิดตัวเลขสำหรับคีย์หลักและยิ่งกว่านั้นถ้า PK นั้นเป็นกลุ่ม (ซึ่งแน่นอนในกรณีของคุณ) อย่างไรก็ตามระดับที่คุณเห็นผลกระทบของการใช้เขตข้อมูลสตริงคือฟังก์ชันของ a) จำนวนแถวในตารางนี้และ b) จำนวนแถวในตารางอื่น ๆ เป็น Foreign Key ที่มีต่อ PK นี้ หากคุณมี 10k แถวในตารางนี้และ 100k แถวในตารางอื่น ๆ ไม่กี่ตารางที่ FK ไปยังตารางนี้ผ่านเขตข้อมูลนั้นบางทีมันอาจจะไม่เป็นที่สังเกตเห็น แต่ผลกระทบเหล่านั้นจะสังเกตเห็นได้ชัดเจนขึ้นเมื่อจำนวนแถวเพิ่มขึ้น

คุณต้องพิจารณาว่าเขตข้อมูลในดัชนีแบบคลัสเตอร์จะถูกส่งไปยังดัชนีที่ไม่ได้เป็นกลุ่ม ดังนั้นคุณไม่ได้ดูข้อมูลมากถึง 40 ไบต์ต่อแถว แต่ (40 * some_number) ไบต์ และในตาราง FK ใด ๆ ที่คุณมี 40 ไบต์เดียวกันในแถวบวกบ่อยกว่าไม่จะมีดัชนีที่ไม่เป็นคลัสเตอร์บนเขตข้อมูลนั้นเนื่องจากมันถูกใช้ใน JOIN ดังนั้นตอนนี้มันจึงเพิ่มเป็นสองเท่าในตารางใด ๆ ที่ FK อันนี้. ถ้าใครอยากจะคิดว่า 40 ไบต์ * 1 ล้านแถว * 10 สำเนานั้นไม่มีอะไรน่ากังวลโปรดอ่านบทความของฉันDisk is Cheap! ORLY? ซึ่งรายละเอียดทั้งหมด (หรืออย่างน้อยที่สุด) ของพื้นที่ได้รับผลกระทบจากการตัดสินใจครั้งนี้

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

ดังนั้นการใช้บางสิ่งบางอย่างCHAR(5)อาจจะเป็นสิ่งที่ดีสำหรับ Clustered PK แต่ส่วนใหญ่ถ้ามันถูกกำหนดด้วยCOLLATE Latin1_General_100_BIN2(หรือบางอย่างเช่นนั้น)

และคุณค่าของ[CODE]การเปลี่ยนแปลงที่เคยสามารถ? ถ้าใช่นั่นคือเหตุผลที่จะไม่ใช้มันเป็น PK (แม้ว่าคุณจะตั้งค่า FKs เป็นON UPDATE CASCADE) หากมันไม่สามารถเปลี่ยนแปลงได้หรือไม่เคยเปลี่ยนแปลง แต่ก็ยังมีเหตุผลมากพอที่จะไม่ใช้มันในแบบของ Clustered PK

แน่นอนคำถามอาจถูกใช้อย่างไม่ถูกต้องเนื่องจากดูเหมือนว่าคุณมีฟิลด์นี้ใน PK ของคุณอยู่แล้ว

โดยไม่คำนึงถึงตัวเลือกที่ดีที่สุดของคุณคือการใช้[ID_CODE]เป็น Clustered PK ใช้ฟิลด์นั้นในตารางที่เกี่ยวข้องเป็น FK และเก็บไว้[CODE]เป็นUNIQUE INDEX(ซึ่งหมายความว่ามันเป็น "คีย์สำรอง")


อัปเดต
ข้อมูลเพิ่มเติมเล็กน้อยตามคำถามนี้ในความคิดเห็นของคำตอบนี้:

[ID_CODE] เป็นคีย์หลักตัวเลือกที่ดีที่สุดหรือไม่ถ้าฉันใช้คอลัมน์ [CODE] เพื่อค้นหาตาราง

ทั้งหมดนี้ขึ้นอยู่กับหลายปัจจัยหลายอย่างที่ฉันได้กล่าวไปแล้ว แต่จะกล่าวซ้ำ:

คีย์หลักคือวิธีระบุแต่ละแถวไม่ว่าจะถูกอ้างอิงโดยคีย์ต่างประเทศใด ๆ ระบบของคุณระบุแถวภายในอย่างไร แต่ไม่จำเป็นต้องเหมือนกับแถวที่ผู้ใช้ระบุตัวเอง / แถวนั้น คอลัมน์ NULL ใด ๆ ที่มีข้อมูลที่ไม่ซ้ำใครสามารถใช้งานได้ แต่มีปัญหาเรื่องการปฏิบัติจริงที่ต้องพิจารณาโดยเฉพาะถ้าในความเป็นจริง PK นั้นอ้างอิงโดย FK ตัวอย่างเช่น GUID นั้นมีเอกลักษณ์และบางคนชอบที่จะใช้มันด้วยเหตุผลต่าง ๆ แต่มันค่อนข้างแย่สำหรับดัชนีแบบกลุ่ม ( NEWSEQUENTIALIDดีกว่า แต่ไม่สมบูรณ์แบบ) ในทางกลับกัน GUID นั้นดีพอ ๆ กับปุ่มสำรองและใช้โดยแอพเพื่อค้นหาแถว แต่ JOIN ก็ยังคงใช้ PK (หรือคล้ายกัน) ในลักษณะ INT

จนถึงตอนนี้คุณยังไม่ได้บอกเราว่า[CODE]เขตข้อมูลเข้ากับระบบจากทุกมุมได้อย่างไรนอกเหนือจากตอนนี้ที่กล่าวถึงว่านี่คือวิธีที่คุณค้นหาแถว แต่มันคือการค้นหาทั้งหมด ดังนั้น:

  • เกี่ยวกับ[CODE]ค่า:

    • มันสร้างขึ้นมาได้อย่างไร?
    • มันเพิ่มขึ้นหรือสุ่ม psuedo?
    • มันยาวหรือยาวแตกต่างกันหรือไม่?
    • ใช้อักขระอะไร
    • หากใช้ตัวอักษรตามตัวอักษร: เป็นตัวพิมพ์เล็กหรือตัวพิมพ์เล็กหรือไม่
    • มันสามารถเปลี่ยนแปลงได้หลังจากใส่เข้าไปหรือไม่?
  • เกี่ยวกับตารางนี้:

    • ทำตารางอื่น ๆ FK ไปที่ตารางนี้หรือไม่? หรือมีการใช้ฟิลด์เหล่านี้ ( [CODE]หรือ[ID_CODE]) ในตารางอื่นแม้ว่าจะไม่ใช่ Foreign Keyed อย่างชัดเจนก็ตาม
    • ถ้า [CODE]เป็นเพียงฟิลด์เดียวที่ใช้เพื่อรับแถวแต่ละแถว[ID_CODE]ฟิลด์นั้นมีจุดประสงค์อะไร หากไม่มีการใช้งานทำไมถึงเป็นอันดับแรก (ซึ่งอาจขึ้นอยู่กับคำตอบของ " [CODE]ฟิลด์สามารถเปลี่ยนแปลงได้หรือไม่")
    • มีกี่แถวในตารางนี้
    • หากตารางอื่นอ้างอิงตารางนี้มีกี่แถวในแต่ละแถว?
    • ดัชนีสำหรับตารางนี้คืออะไร

การตัดสินใจนี้ไม่สามารถทำได้อย่างหมดจดกับคำถามของ "NVARCHAR ใช่หรือไม่" ฉันจะพูดอีกครั้งว่าโดยทั่วไปการพูดฉันไม่คิดว่ามันจะเป็นความคิดที่ดี แต่มีบางครั้งที่มันใช้ได้ เนื่องจากบางฟิลด์ในตารางนี้ไม่น่าเป็นไปได้ที่จะมีดัชนีมากกว่านี้หรืออย่างน้อยก็ไม่มาก ดังนั้นคุณอาจปรับวิธีการ[CODE]เป็นดัชนีแบบคลัสเตอร์ และถ้าไม่มีตารางอื่นอ้างอิงตารางนี้คุณก็อาจจะทำให้มันเป็น PK แต่ถ้าตารางอื่นอ้างอิงตารางนี้ฉันจะเลือกใช้[ID_CODE]ฟิลด์เป็น PK แม้ว่าจะไม่เป็นคลัสเตอร์


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

ขอบคุณสำหรับคำตอบ. คือ[ID_CODE]เป็นPRIMARY KEYตัวเลือกที่ดีที่สุดถ้าผมใช้[CODE]คอลัมน์ที่จะมองขึ้นโต๊ะได้หรือไม่
VansFannel

@VansFannel โปรดดูการอัปเดตของฉัน ขอบคุณ
โซโลมอน Rutzky

ฉันเข้าร่วมชุมชน dba นี้เพื่อโหวตคำตอบนี้
Ahmet Arslan

6

คุณต้องแยกแนวคิด:

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

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

ไม่จำเป็นสำหรับคีย์หลักเพื่อเป็นดัชนีคลัสเตอร์ คุณสามารถID_CODEเป็น PK และ(CODE_LEVEL, CODE)เป็นคีย์คลัสเตอร์ หรือวิธีอื่น ๆ

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

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

สำหรับตัวเลือกคีย์หลักไม่ควรมีปัญหา: ตัวแบบข้อมูลของคุณตรรกะแอปของคุณควรกำหนดว่าคีย์หลักคืออะไร

ที่ถูกกล่าวว่า 2c ฉัน: NVARCHAR(20)คือไม่กว้าง เป็นขนาดคีย์ที่ยอมรับได้อย่างสมบูรณ์แบบแม้กระทั่งสำหรับตารางขนาดใหญ่


ขอบคุณสำหรับคำตอบ. คือ[ID_CODE]เป็นPRIMARY KEYตัวเลือกที่ดีที่สุดถ้าผมใช้[CODE]คอลัมน์ (และอาจจะ[CODE_LEVEL]) จะมองขึ้นโต๊ะได้หรือไม่
VansFannel

@VansFannel เพียงคุณเท่านั้นที่สามารถตอบได้
Remus Rusanu

แต่ในความคิดของคุณ ...
VansFannel

2
ความคิดเห็นของฉันจะต้องพิจารณา DDL ที่แน่นอนของตารางทั้งหมดและดัชนีทั้งหมดคีย์ต่างประเทศที่อ้างอิงตัวเลขจำนวนแถวโดยประมาณปริมาณงานแบบสอบถามที่คาดหวังแอปพลิเคชัน SLA ที่คาดหวังและไม่ได้ใช้งบประมาณน้อยที่สุดสำหรับฮาร์ดแวร์
Remus Rusanu

ขอบคุณ ฉันจะใช้[CODE]คอลัมน์เป็นคีย์หลัก
VansFannel

4

ฉันจะไม่อนุญาตให้ใครทำnvarchar(20)เพื่อเป็น PK ในฐานข้อมูลของฉัน คุณเสียพื้นที่ดิสก์และหน่วยความจำแคช ทุกดัชนีในตารางนี้และค่า FK ทั้งหมดที่ทำซ้ำค่า wide นี้ อาจจะเป็นถ่าน (20) ถ้าพวกเขาสามารถพิสูจน์ได้ คุณพยายามเก็บข้อมูลประเภทCODEใด คุณต้องการจัดเก็บอักขระ nvarchar จริง ๆ หรือไม่ ฉันมักจะทำให้ค่า PKs "ภายใน" ที่ผู้ใช้ไม่เห็นและฉันพยายามเก็บค่าที่แสดงแยกต่างหาก บางครั้งค่าที่แสดงต้องเปลี่ยนแปลงซึ่งกลายเป็นปัญหาอย่างมากกับ PKs + FK

นอกจากนี้คุณทราบหรือไม่ว่า 'ตัวตนขนาดใหญ่ (1,1)' สามารถเพิ่มขึ้นได้ถึง 9,223,372,036,854,775,807?

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

นอกจากว่าคุณกำลังสร้างฐานข้อมูลนี้สำหรับ Google จะไม่เป็นเรื่องปกติที่int identity (1,1)มีวงเงินเกิน 2 พันล้านหรือไม่


int คือ 4 ไบต์ใน SQL ซึ่งให้ -2.1 พันล้านถึง + 2.1 พันล้าน
datagod

@datagod ฮ่าขอบคุณตัวเลขมากมายที่ฉันนับผิด!
ไม่มี ID ในระบบนี้

ขอบคุณสำหรับคำตอบ. คือ[ID_CODE]เป็นPRIMARY KEYตัวเลือกที่ดีที่สุดถ้าผมใช้[CODE]คอลัมน์ที่จะมองขึ้นโต๊ะได้หรือไม่ ขอบคุณ
VansFannel

ฉันเคยอยู่ในเรือลำนี้จนกระทั่งฉันมีคนใช้ลักษณะตามลำดับของ "int" เพื่อทำนายข้อมูล / ผู้ใช้ในฐานข้อมูลของฉันและเก็บเกี่ยวทุกอย่างที่ฉันมี ไม่มีอีกครั้ง. การเผชิญหน้ากับฐานข้อมูลสาธารณะจำเป็นต้องเป็นเรื่องยากขึ้นเล็กน้อยในการดึงข้อมูลออกมา
DaBlue

3

ไม่ควรมีการลงโทษโดยธรรมชาติ / สังเกตเห็นได้นอกเหนือจากคุณเสี่ยงที่จะใช้คีย์ไวด์เมื่อใช้ nvarchar / varchar หากไม่ทราบ โดยเฉพาะอย่างยิ่งถ้าคุณเริ่มรวมพวกมันในคีย์ผสม

แต่ในตัวอย่างของคุณที่มีความยาว (20) คุณควรจะสบายดีและฉันจะไม่กังวลอะไรมาก เพราะถ้า CODE เป็นวิธีที่คุณสืบค้นข้อมูลเป็นหลัก - ดัชนีกลุ่มที่ฟังดูสมเหตุสมผลมาก

อย่างไรก็ตามคุณควรพิจารณาว่าคุณต้องการให้มันเป็นคีย์หลักหรือดัชนีที่ไม่ซ้ำกัน (คลัสเตอร์) มีความแตกต่าง (เล็ก) ระหว่างดัชนีคลัสเตอร์และคีย์หลัก (โดยทั่วไป - คีย์หลักระบุข้อมูลของคุณ แต่ดัชนีเป็นวิธีที่คุณค้นหาข้อมูล) ดังนั้นหากคุณต้องการให้ ID_Code เป็นคีย์หลักและ สร้างดัชนีคลัสเตอร์ที่ไม่ซ้ำกับ CODE (หมายเหตุ: SQL Server จะทำให้คีย์หลักของคุณเป็นดัชนีคลัสเตอร์โดยอัตโนมัติเว้นแต่ว่าคุณได้สร้างดัชนีคลัสเตอร์ด้วยตัวเอง)

นอกจากนี้ให้พิจารณาว่าคุณต้องการ ID_Code จริงๆตอนนี้คุณมีรหัสที่ไม่ซ้ำกันหรือไม่


2
ที่จริงแล้วNVARCHAR(20)มีขนาด 40ไบต์ (สูงสุด) และเนื่องจากเป็นคอลัมน์ที่มีความยาวผันแปรจึงไม่ใช่ตัวเลือกที่ดีที่สุดสำหรับดัชนีคลัสเตอร์ ID_CODEเป็นBIGINT IDENTITYจะเป็นที่ดีมากเลือกที่นี่!
marc_s

ฉันรู้ว่ามันมีขนาด 40 ไบต์ แต่ก็ไม่มีเหตุผลอะไรที่จะระบุได้เพราะมันไม่มีที่ไหนใกล้ 900 ไบต์ และถ้าคุณทำการสืบค้นข้อมูลจาก CODE เป็นหลักมันจะเป็นทางเลือกที่ดีกว่าในการหลีกเลี่ยงการทำดัชนีซ้ำซ้อนเพื่อรักษาไว้เพราะคุณยังต้องการดัชนีอยู่และจากนั้นคุณต้องค้นหาผ่าน aftwards ของคลัสเตอร์
Allan S Hansen

มูลค่าการกล่าวขวัญ - ซึ่งฉันลืมที่จะพูดถึงและที่ฉันสงสัยว่าที่ @marc_s กำลังพูดถึงคือดัชนีประเภทนี้สามารถนำไปสู่การกระจายตัวของดัชนีขนาดใหญ่กว่าตัวตนตามลำดับ แต่ฉันยังเห็นว่ามันเป็นดัชนีที่สมเหตุสมผลในสถานการณ์เฉพาะนี้ บนปัจจัยการสืบค้น
Allan S. Hansen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.