Kolom NVARCHAR sebagai KUNCI UTAMA atau sebagai kolom UNIK

11

Saya sedang mengembangkan database SQL Server 2012 dan saya ragu tentang kolom nvarchar sebagai kunci utama.

Saya punya tabel ini:

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

Tapi sekarang saya ingin menggunakan [CODE]kolom sebagai kunci utama dan menghapus [ID_CODE]kolom.

Apakah ada masalah atau hukuman jika saya memiliki NVARCHARkolom PRIMARY KEY?

[CODE]nilai kolom harus unik, jadi saya pikir saya bisa menetapkan UNIQUEbatasan untuk kolom itu.

Apakah saya harus menggunakan [CODE]sebagai kunci utama atau lebih baik jika saya menetapkan UNIQUEbatasan pada [CODE]kolom?

VansFannel
sumber
1
Yang cukup penting dalam pertimbangan adalah berapa banyak baris yang akan ada di meja Anda?
James Z
Ini bukan jawaban semata , tetapi saya cenderung berpikir bahwa CODEkolom Anda harus unik, tetapi bukan Kunci Utama. Saya menduga itu membawa informasi. Jika informasi itu dapat diubah, maka Anda CODEharus berubah atau ketinggalan zaman. Itu akan membuat Kunci Utama Anda mudah menguap, dan saya tidak bisa melihat itu berakhir dengan baik. Yang terbaik adalah membiarkan PK Anda hanya menjadi kunci, dan KODE Anda dapat melakukan apa yang disukainya. Hanya sebuah opini.
Manngo
@Manngo, terima kasih atas komentar Anda. Ya, saya melakukannya seperti itu: ID_CODE adalah kunci utama dan CODE adalah UNIK.
VansFannel

Jawaban:

13

Ya, benar-benar ada konsekuensi negatif untuk menggunakan string, bukan tipe numerik untuk Kunci Utama, dan terlebih lagi jika PK itu Clustered (yang memang dalam kasus Anda). Namun, sejauh mana Anda melihat efek (s) menggunakan bidang string adalah fungsi dari a) berapa banyak baris dalam tabel ini, dan b) berapa banyak baris dalam tabel lain yang Asing Kunci untuk PK ini. Jika Anda hanya memiliki 10k baris di tabel ini dan 100k baris di beberapa tabel lain yang FK ke tabel ini melalui bidang itu, maka mungkin itu tidak akan begitu terlihat. Tetapi efek-efek itu tentu saja menjadi lebih nyata ketika jumlah baris meningkat.

Anda perlu mempertimbangkan bahwa bidang dalam Indeks Clustered dibawa ke Indeks Non-Clustered. Jadi Anda tidak hanya melihat hingga 40 byte per baris, tetapi (40 * some_number) byte. Dan di setiap tabel FK Anda memiliki 40 byte yang sama di baris plus lebih sering daripada tidak akan ada indeks Non-Clustered pada bidang itu seperti yang digunakan dalam GABUNGAN, jadi sekarang benar-benar dua kali lipat dalam setiap tabel yang FK untuk yang ini. Jika seseorang cenderung berpikir bahwa 40 byte * 1 juta baris * 10 salinannya tidak perlu dikhawatirkan, silakan lihat artikel saya Disk Is Cheap! ORLY? yang merinci semua (atau paling tidak sebagian besar) area yang terkena dampak keputusan ini.

Hal lain yang perlu dipertimbangkan adalah bahwa pemfilteran dan pengurutan pada string, terutama ketika tidak menggunakan Kolasi biner (saya berasumsi Anda menggunakan default database yang biasanya case-insensitive) jauh lebih efisien (yaitu membutuhkan waktu lebih lama) daripada ketika menggunakan INT/ BIGINT. Ini memengaruhi semua kueri yang memfilter / bergabung / mengurutkan di bidang ini.

Oleh karena itu, menggunakan sesuatu seperti CHAR(5)mungkin akan baik-baik saja untuk PK Clustered, tetapi kebanyakan jika itu juga didefinisikan dengan COLLATE Latin1_General_100_BIN2(atau sesuatu seperti itu).

Dan dapatkah nilai [CODE]berubah? Jika ya maka itu adalah alasan yang lebih besar untuk tidak menggunakannya sebagai PK (bahkan jika Anda mengatur agar FK ON UPDATE CASCADE). Jika tidak bisa atau tidak akan pernah berubah itu baik-baik saja, tetapi masih ada lebih dari cukup alasan untuk tidak menggunakannya sebagai PK Clustered.

Tentu saja, pertanyaannya mungkin tidak tepat diungkapkan karena tampaknya Anda sudah memiliki bidang ini di PK Anda.

Apapun, pilihan terbaik Anda, sejauh ini, adalah menggunakan [ID_CODE]sebagai PK Clustered, gunakan bidang itu dalam tabel terkait sebagai FK, dan simpan [CODE]sebagai UNIQUE INDEX(yang berarti itu adalah "kunci alternatif").


Perbarui
sedikit lebih banyak info berdasarkan pertanyaan ini di komentar pada jawaban ini:

Apakah [ID_CODE], sebagai PRIMARY KEY, opsi terbaik jika saya menggunakan kolom [KODE] untuk mencari tabel?

Ini semua tergantung pada banyak faktor, beberapa di antaranya telah saya sebutkan tetapi akan menyatakan kembali:

Kunci Utama adalah bagaimana setiap baris diidentifikasi, terlepas apakah itu dirujuk oleh Kunci Asing atau tidak. Bagaimana sistem Anda mengidentifikasi baris secara internal terkait dengan, tetapi tidak harus sama dengan, bagaimana pengguna Anda mengidentifikasi diri / baris itu. Setiap kolom TIDAK NULL dengan data unik dapat berfungsi, tetapi ada masalah kepraktisan yang perlu dipertimbangkan, terutama jika PK, pada kenyataannya, dirujuk oleh setiap FK. Misalnya GUID unik dan beberapa orang sangat suka menggunakannya karena berbagai alasan, tetapi mereka cukup buruk untuk Indeks Clustered ( NEWSEQUENTIALIDlebih baik, tetapi tidak sempurna). Di sisi lain, GUID baik-baik saja sebagai kunci alternatif dan digunakan oleh aplikasi untuk mencari baris, tetapi GABUNGAN masih dilakukan dengan menggunakan INT (atau yang serupa) PK.

Sejauh ini Anda belum memberi tahu kami bagaimana [CODE]bidang cocok ke sistem dari semua sudut, di luar sekarang menyebutkan bahwa ini adalah bagaimana Anda mencari baris, tetapi apakah itu untuk semua pertanyaan atau hanya beberapa? Karenanya:

  • Mengenai [CODE]nilai:

    • Bagaimana ini dihasilkan?
    • Apakah ini tambahan atau psuedo-acak?
    • Apakah panjangnya seragam atau panjangnya bervariasi?
    • Karakter apa yang digunakan?
    • Jika menggunakan karakter alfabet: apakah case-sensitive atau tidak sensitif?
    • Bisakah itu berubah setelah dimasukkan?
  • Mengenai tabel ini:

    • Apakah ada tabel lain yang FK ke tabel ini? Atau apakah bidang ini ( [CODE]atau [ID_CODE]) digunakan dalam tabel lain, meskipun tidak secara eksplisit Asing Kunci?
    • Jika [CODE] satu-satunya bidang yang digunakan untuk mendapatkan baris individual, lalu tujuan apa yang [ID_CODE]dilayani bidang tersebut? Jika tidak digunakan, mengapa harus di tempat pertama (yang mungkin tergantung pada jawaban untuk "Bisakah [CODE]bidang berubah?")?
    • Berapa banyak baris dalam tabel ini?
    • Jika tabel lain merujuk tabel ini, berapa banyak dan berapa baris di masing-masingnya?
    • Apa indeks untuk tabel ini?

Keputusan ini tidak dapat dibuat murni berdasarkan pertanyaan "NVARCHAR ya atau tidak?". Saya lagi akan mengatakan bahwa secara umum saya tidak menganggapnya sebagai ide yang baik, tetapi ada kalanya tidak masalah. Mengingat begitu sedikit bidang dalam tabel ini, tidak mungkin ada lebih banyak, atau setidaknya tidak banyak, indeks. Jadi Anda mungkin baik-baik saja untuk memiliki [CODE]Indeks Clustered. Dan jika tidak ada tabel lain referensi tabel ini maka Anda mungkin juga baik-baik saja menjadikannya PK. Tapi, jika tabel lain merujuk tabel ini maka saya akan memilih [ID_CODE]bidang sebagai PK, bahkan jika Non-Clustered.

Solomon Rutzky
sumber
Apakah downvoter anonim (yang tampaknya juga memilih-turun @noIDonthissystem 's jawaban) peduli untuk menawarkan kritik konstruktif atau menunjukkan beberapa logika yang cacat?
Solomon Rutzky
Terima kasih atas jawaban anda. Apakah [ID_CODE], sebagai PRIMARY KEY, opsi terbaik jika saya menggunakan [CODE]kolom untuk mencari tabel?
VansFannel
@ VanFannel, silakan lihat pembaruan saya. Terima kasih.
Solomon Rutzky
Saya bergabung dengan komunitas dba ini hanya untuk meningkatkan jawaban ini.
Ahmet Arslan
6

Anda harus memisahkan konsep:

  • kunci utama adalah konsep desain , properti logis dari entri dalam tabel. Itu harus tetap selama masa entri tabel, dan harus menjadi kunci yang digunakan dalam aplikasi untuk referensi entri.

  • indeks berkerumun adalah konsep penyimpanan , properti fisik. Itu harus menjadi jalur akses paling umum untuk kueri, itu harus berfungsi untuk memenuhi sebagai meliputi indeks untuk sebagian besar kasus, dan memenuhi sebanyak mungkin berbagai kueri.

Tidak diperlukan kunci primer untuk menjadi indeks berkerumun. Anda dapat memiliki ID_CODEsebagai PK dan (CODE_LEVEL, CODE)sebagai kunci berkerumun. Atau sebaliknya.

Kunci cluster yang lebih besar memiliki beberapa dampak negatif, karena kunci yang lebih luas berarti kepadatan yang lebih rendah pada halaman indeks dan ukuran yang lebih besar dikonsumsi pada semua indeks non-cluster. sudah ada banyak tinta yang tumpah pada topik ini, misalnya. mulai dari Lebih banyak pertimbangan untuk kunci pengelompokan - perdebatan indeks berkerumun berlanjut! .

Tetapi intinya adalah bahwa pilihan kunci indeks berkerumun adalah trade-off. Di satu sisi Anda memiliki persyaratan ukuran penyimpanan, dengan dampak umum kinerja (key yang lebih besar -> ukuran yang lebih besar -> lebih IO, dan IO bandwidth mungkin yang sumber daya yang paling langka Anda memiliki). Di sisi lain memilih kunci berkerumun yang salah atas nama penghematan ruang dapat memiliki konsekuensi kinerja kueri, seringkali lebih buruk daripada masalah yang dihasilkan dari kunci lebar.

Adapun pilihan kunci utama, seharusnya tidak menjadi masalah: model data Anda, logika aplikasi Anda, harus menentukan apa kunci utama itu.

Yang sedang berkata, 2c saya: NVARCHAR(20)ini tidak lebar. Merupakan ukuran kunci cluster yang dapat diterima, bahkan untuk meja besar.

Remus Rusanu
sumber
Terima kasih atas jawaban anda. Apakah [ID_CODE], sebagai PRIMARY KEY, opsi terbaik jika saya menggunakan [CODE]kolom (dan mungkin [CODE_LEVEL]) untuk melihat tabel?
VansFannel
@ VanFannel hanya Anda yang bisa menjawabnya.
Remus Rusanu
Tapi menurut Anda ...
VansFannel
2
Pendapat saya harus mempertimbangkan DDL yang tepat dari seluruh tabel dan semua indeks, kunci asing yang merujuknya, perkiraan jumlah baris, beban kerja kueri yang diharapkan, aplikasi mengharapkan SLA dan tidak sedikit yang tersedia untuk hardware dan lisensi.
Remus Rusanu
Terima kasih. Saya akan menggunakan [CODE]kolom sebagai KUNCI UTAMA.
VansFannel
4

Saya tidak akan pernah mengizinkan siapa pun nvarchar(20)untuk menjadi seorang PK di database saya. Anda membuang ruang disk dan memori cache. Setiap indeks pada tabel ini dan semua FK untuknya mereplikasi nilai luas ini. Mungkin char (20) jika mereka bisa membenarkannya. Data apa yang Anda coba simpan CODE? Apakah Anda benar-benar perlu menyimpan karakter nvarchar? Saya cenderung membuat nilai PK "internal" tidak terlihat oleh pengguna, dan saya mencoba untuk menjaga nilai yang ditampilkan terpisah. Nilai yang ditampilkan terkadang perlu diubah, yang menjadi sangat bermasalah dengan PK + FK.

Juga, apakah Anda menyadari bahwa 'identitas bigint (1,1)' dapat meningkat hingga 9.223.372.036.854.775.807?

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

Kecuali jika Anda membangun basis data ini untuk Google, bukankah normal int identity (1,1)dengan batas lebih dari 2 Milyarnya sudah cukup?

tidak ada ID pada sistem ini
sumber
int adalah 4 byte dalam SQL, yang memberi Anda -2,1 miliar hingga + 2,1 miliar.
datagod
@dagagod, terima kasih, begitu banyak digit yang saya hitung salah!
tidak ada ID pada sistem ini
Terima kasih atas jawaban anda. Apakah [ID_CODE], sebagai PRIMARY KEY, opsi terbaik jika saya menggunakan [CODE]kolom untuk mencari tabel? Terima kasih.
VansFannel
Saya dulu berada di kapal ini sampai saya memiliki seseorang menggunakan sifat berurutan "int" untuk memprediksi data / pengguna di DB saya dan memanen hampir semua yang saya miliki. Tidak akan lagi. Publik yang menghadapi kebutuhan DB harus sedikit lebih sulit untuk mendapatkan informasi.
DaBlue
3

Seharusnya tidak ada hukuman inheren / nyata selain Anda berisiko menggunakan kunci lebar saat menggunakan nvarchar / varchar jika tidak sadar. Terutama jika Anda mulai menggabungkannya dengan kunci komposit.

Tetapi dalam contoh Anda (20) panjang Anda harus baik-baik saja dan saya tidak akan terlalu khawatir tentang hal itu. Karena jika CODE adalah bagaimana Anda terutama meminta data Anda - indeks berkerumun pada itu terdengar sangat masuk akal.

Namun, Anda harus mempertimbangkan apakah Anda benar-benar menginginkannya sebagai kunci utama atau hanya indeks unik (berkerumun). Ada perbedaan (kecil) antara indeks berkerumun dan kunci utama (pada dasarnya - kunci utama mengidentifikasi data Anda, tetapi indeks adalah cara Anda melakukan kueri data), jadi jika Anda mau, Anda bisa dengan mudah menjadikan ID_Code Anda sebagai kunci utama dan buat indeks berkerumun unik di atas KODE. (perhatikan: SQL Server akan secara otomatis membuat Kunci Utama Anda menjadi indeks berkerumun, kecuali Anda telah membuat sendiri indeks berkerumun secara manual)

Juga pertimbangkan apakah Anda benar-benar membutuhkan ID_Code sekarang Anda memiliki KODE yang unik.

Allan S. Hansen
sumber
2
Sebenarnya, NVARCHAR(20)adalah 40 byte dalam ukuran (maks), dan karena itu kolom panjang variabel , itu bukan pilihan terbaik untuk indeks berkerumun. ID_CODEmenjadi BIGINT IDENTITYakan menjadi pilihan yang jauh lebih baik di sini!
marc_s
Saya tahu ini 40 byte, tetapi tidak ada banyak alasan untuk menentukannya, karena tidak ada yang mendekati 900 byte. Dan jika Anda terutama meminta data dari CODE, itu akan menjadi pilihan yang lebih baik untuk menghindari memiliki indeks yang berlebihan untuk dipelihara, karena Anda masih memerlukan indeks di dalamnya, dan kemudian Anda harus mencari melalui bagian belakang yang berkerumun
Allan S. Hansen
Layak disebutkan - yang saya lupa sebutkan dan yang saya duga adalah alamat @marc_s adalah bahwa indeks jenis ini dapat menyebabkan fragmentasi indeks yang lebih besar daripada identitas berurutan, tapi saya masih melihatnya sebagai indeks yang masuk akal dalam situasi khusus ini berdasarkan pada faktor kueri.
Allan S. Hansen