Apakah masuk akal untuk menandai semua kolom kecuali satu sebagai kunci utama?

9

Saya punya meja yang mewakili film. Bidang adalah:
id (PK), title, genre, runtime, released_in, tags, origin, downloads.

Basis data saya tidak dapat dicemari oleh baris yang digandakan, jadi saya ingin menegakkan keunikan. Masalahnya adalah bahwa film yang berbeda dapat memiliki judul yang sama, atau bahkan bidang yang sama kecuali tagsdan downloads. Bagaimana cara menegakkan keunikan?

Saya memikirkan dua cara:

  • buat semua bidang kecuali downloadskunci utama. Saya menahan downloadskarena JSON dan mungkin akan berdampak pada kinerja.
  • simpan hanya idsebagai kunci utama, tetapi tambahkan batasan unik dengan semua kolom lainnya (kecuali, sekali lagi, downloads).

Saya membaca pertanyaan ini yang sangat mirip, tetapi saya tidak begitu mengerti apa yang harus saya lakukan. Saat ini tabel ini tidak terkait dengan tabel lain, tetapi di masa depan bisa jadi.

Saat ini saya memiliki kurang dari 20.000 catatan, tetapi saya berharap jumlahnya akan bertambah. Saya tidak tahu apakah ini agak relevan dengan masalah ini.

EDIT: Saya memodifikasi skema dan di sini adalah bagaimana saya akan membuat tabel:

CREATE TABLE movies (
    id          serial PRIMARY KEY,
    title       text NOT NULL,
    runtime     smallint NOT NULL CHECK (runtime >= 0),
    released_in smallint NOT NULL CHECK (released_in > 0),
    genres      text[] NOT NULL default ARRAY[]::text[],
    tags        text[] NOT NULL default ARRAY[]::text[],
    origin      text[] NOT NULL default ARRAY[]::text[],
    downloads   json NOT NULL,
    inserted_at timestamp NOT NULL default current_timestamp,
    CONSTRAINT must_be_unique UNIQUE(title,runtime,released_in,genres,tags,origin)
);

Saya juga menambahkan timestampkolom, tapi itu tidak masalah karena saya tidak akan menyentuhnya. Jadi itu akan selalu otomatis dan unik.

rubik
sumber
Pertanyaan yang terkait erat (dengan jawaban) pada SO: Apakah saya memerlukan kunci utama untuk tabel saya, yang memiliki UNIK (gabungan 4-kolom), yang salah satunya bisa NULL? . Jika ada kolom yang NULL, segera pertimbangkan ini: dba.stackexchange.com/q/9759/3684 .
Erwin Brandstetter

Jawaban:

4

Definisi tabel Anda terlihat masuk akal sekarang. Dengan semua kolom NOT NULL, UNIQUEkendala akan berfungsi seperti yang diharapkan - kecuali untuk kesalahan ketik dan perbedaan kecil dalam pengejaan, yang mungkin agak umum, saya khawatir. Pertimbangkan komentar @ a_horse .

Alternatif dengan indeks unik fungsional

Pilihan lainnya adalah indeks unik fungsional (mirip dengan apa yang @Dave komentari ). Tetapi saya akan menggunakan uuidtipe data untuk mengoptimalkan ukuran dan kinerja indeks.

Para pemain dari array ke teks tidak IMMUTABLE(karena implementasi generiknya):

Karenanya, Anda memerlukan fungsi pembantu kecil untuk menyatakannya tidak berubah:

CREATE OR REPLACE FUNCTION f_movie_uuid(_title text
                                      , _runtime int2
                                      , _released_in int2
                                      , _genres text[]
                                      , _tags text[]
                                      , _origin text[])
  RETURNS uuid LANGUAGE sql IMMUTABLE AS  -- faking IMMUTABLE
'SELECT md5(_title || _runtime::text || _released_in::text
         || _genres::text || _tags::text || _origin::text)::uuid';

Gunakan untuk definisi indeks:

CREATE UNIQUE INDEX movies_uni_idx
ON movies (f_movie_uuid(title,runtime,released_in,genres,tags,origin));

SQL Fiddle.

Keterangan lebih lanjut:

Anda mungkin menggunakan UUID yang dihasilkan sebagai PK, tetapi saya masih akan menggunakan serialkolom dengan 4 byte, yang sederhana dan murah untuk referensi FK dan tujuan lain. UUID akan menjadi pilihan bagus untuk sistem terdistribusi yang perlu menghasilkan nilai PK secara mandiri. Atau untuk meja yang sangat besar, tetapi tidak ada cukup film di tata surya kita untuk itu.

Pro dan kontra

Sebuah kendala yang unik diimplementasikan dengan indeks yang unik pada terlibat kolom. Masukkan kolom yang relevan dalam definisi kendala terlebih dahulu dan Anda memiliki indeks yang berguna untuk tujuan lain sebagai manfaat jaminan.

Ada manfaat spesifik lainnya, berikut adalah daftar:

The indeks unik fungsional adalah (berpotensi jauh) lebih kecil dalam ukuran, yang dapat membuatnya secara substansial lebih cepat. Jika kolom Anda tidak terlalu besar, perbedaannya tidak akan banyak. Ada juga biaya overhead kecil untuk perhitungan.

Menggabungkan semua kolom dapat memperkenalkan false positive ( 'foo ' || 'bar' = 'foob ' || 'ar', tapi sepertinya sangat tidak mungkin untuk kasus ini. Kesalahan ketik jauh lebih mungkin sehingga Anda dapat dengan aman mengabaikannya di sini.

Keunikan dan susunan

Array harus disortir secara konsisten agar masuk akal dalam pengaturan unik yang bergantung pada =operator karena '{1,2}' <> '{2,1}'. Saya sarankan mencari tabel genre, tagdan origindengan serialPK dan entri unik, yang memungkinkan pencarian fuzzy untuk elemen array. Kemudian:

Either way, bekerja dengan array secara langsung atau dengan skema yang dinormalisasi dan pandangan yang terwujud, pencarian bisa sangat efisien dengan indeks dan operator yang tepat:

Ke samping

Jika Anda menggunakan Postgres 9.4 atau lebih baru pertimbangkan untuk jsonbmenggunakannyajson .

Erwin Brandstetter
sumber
6

Bayangkan Anda keluar dengan sekelompok teman dan percakapan berubah menjadi film. Seseorang bertanya, "Apa pendapatmu tentang 'The Three Musketeers'?" Anda menjawab, "Yang mana?"

Informasi tambahan apa yang Anda perlukan untuk benar-benar yakin bahwa Anda berdua memikirkan film yang sama? Nama direktur? Studio produksi? Tahun dirilis? Salah satu nama bintang itu? Kombinasi dua atau lebih?

Jawaban atas pertanyaan saya dan pertanyaan Anda sama.

Namun, saya tidak akan berpikir bahwa genre akan menjadi kandidat yang baik. Salah satu alasannya, genre adalah kriteria yang terlalu subyektif. Apakah 'The Three Musketeers' action? drama? petualangan? komedi? aksi petualangan? komedi romantis? Saya sering melihat film yang sama terdaftar di bawah genre yang berbeda. Bahkan ketika Anda mengizinkan beberapa genre, pengguna Anda dapat memilih yang sepenuhnya berbeda yang tidak terdaftar dengan film aktual yang mereka cari.

Bahkan runtimes dapat berbeda, terutama antara versi teater dan VCR / DVD / b-ray.

Jadi Anda perlu atribut yang keras dan objektif yang tidak akan berubah dari satu rilis media ke yang lainnya. Sayangnya, itu dapat mengecualikan nama film karena film telah dikenal untuk diganti namanya, terutama setelah rilis sekuel.

Bagaimana dengan tanggal rilis? Rilis teater tahun 1993? Rilis VCR tahun 1999? Rilis DVD tahun 2004? Anda mendapatkan idenya.

Kalau dipikir-pikir, bagaimana dengan semua film yang disutradarai oleh Alan Smithee? Pernahkah sutradara yang sesungguhnya akhirnya melangkah maju untuk memasukkan namanya pada proyek setelah fakta? Saya tidak tahu

Hmm, lebih baik aku berhenti sementara masih ada beberapa kriteria yang tersisa.

Beberapa poin tambahan:

  • Ya, simpan kunci pengganti dan buat indeks unik pada bidang kunci alami (jika Anda akhirnya dapat memakukannya). Kunci pengganti adalah yang terbaik untuk referensi kunci asing. Anda tidak ingin menduplikasi semua bidang kunci alami di setiap tabel yang berisi referensi ke film.
  • Lepaskan bidang array (genre, tag, asal). Silakan menormalkan kembali atribut-atribut tersebut dengan benar. Saya belum pernah melihat bidang array yang bukan masalah lebih banyak daripada nilainya, terutama jika Anda ingin mereka dapat dicari ("... di mana genre = 'horor' ..."). Catatan ini tidak akan secara otomatis menghilangkan masalah dengan perbedaan huruf dan ejaan ("Fiksi Ilmiah" vs "Sains") - kecuali Anda mempertahankan tabel pencarian dengan benar . Tetapi jauh lebih mudah untuk memeriksa perbedaan seperti itu dalam satu bidang tabel kecil daripada setiap sel array dari setiap baris tabel besar.
TommCatt
sumber
4

Kolom ID tidak memiliki keuntungan sama sekali dalam hal keunikan yang ingin / perlu Anda lakukan. Keunikan kombinasi atribut apa pun tidak akan pernah ditegakkan dengan menambahkan ID yang tidak berarti. "Keuntungannya" hanya menunjukkan ketika Anda sampai ke titik di mana Anda akan membutuhkan tabel baru yang memerlukan kunci asing untuk yang satu ini. Dalam hal ini, dan JIKA Anda telah memasukkan Id, maka Anda dapat menggunakannya sebagai FK di tabel baru Anda. (Tapi jangan berpikir itu akan menjadi makan siang gratis. Kelemahan dari pendekatan semacam itu adalah Anda mungkin akan menemukan diri Anda menulis lebih banyak untuk tujuan sekadar mengambil informasi yang bisa saja menjadi bagian dari tabel baru yang Anda buat. )

Erwin Smout
sumber
1
Jika aturan bisnis mengatakan bahwa kombinasi nilai dalam atribut FOO dan BAR harus unik, maka menambahkan ID tidak akan mencapainya. Menambahkan ID hanya memudahkan menghindari keharusan memasukkan FOO dan BAR dalam tabel referensi. Yang pada gilirannya mengharuskan lebih banyak bergabung karena atribut FOO dan BAR (yang membawa pengidentifikasi BISNIS) tidak berada di tempat mereka seharusnya (dan di mana mereka sangat mungkin DIHARAPKAN berada, setidaknya dari sudut pandang bisnis).
Erwin Smout
1
BUKAN "baris" yang harus unik, yang dikatakan bisnis adalah pengidentifikasi mereka. Jika itu adalah kombinasi atribut FOO dan BAR, maka itu adalah kombinasi atribut FOO dan BAR.
Erwin Smout
2
Memiliki ID atau tidak tidak memecahkan masalah penegakan keunikan kolom "bisnis" di tabel Anda. Penegakan keunikan harus dilakukan dengan mendeklarasikan kunci yang sesuai (yang Anda lakukan - fakta bahwa Anda menggunakan kata sintaksis "CONSTRAINT" bukan "KEY" tidak berarti itu bukan kunci).
Erwin Smout