Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Mungkinkah melakukan kunci asing MySQL ke salah satu dari dua tabel yang mungkin?

Apa yang Anda gambarkan disebut Asosiasi Polimorfik. Artinya, kolom "foreign key" berisi nilai id yang harus ada di salah satu kumpulan tabel target. Biasanya tabel target terkait dalam beberapa cara, seperti menjadi instance dari beberapa superclass data yang umum. Anda juga memerlukan kolom lain di samping kolom kunci asing, sehingga pada setiap baris, Anda dapat menentukan tabel target mana yang dirujuk.

CREATE TABLE popular_places (
  user_id INT NOT NULL,
  place_id INT NOT NULL,
  place_type VARCHAR(10) -- either 'states' or 'countries'
  -- foreign key is not possible
);

Tidak ada cara untuk memodelkan Asosiasi Polimorfik menggunakan batasan SQL. Batasan kunci asing selalu merujuk satu tabel target.

Asosiasi Polimorfik didukung oleh kerangka kerja seperti Rails dan Hibernate. Tetapi mereka secara eksplisit mengatakan bahwa Anda harus menonaktifkan batasan SQL untuk menggunakan fitur ini. Sebaliknya, aplikasi atau kerangka kerja harus melakukan pekerjaan yang setara untuk memastikan bahwa referensi terpenuhi. Artinya, nilai dalam kunci asing ada di salah satu tabel target yang mungkin.

Asosiasi Polimorfik lemah dalam hal menegakkan konsistensi basis data. Integritas data bergantung pada semua klien yang mengakses database dengan logika integritas referensial yang sama yang diterapkan, dan juga penegakannya harus bebas bug.

Berikut adalah beberapa solusi alternatif yang memanfaatkan integritas referensial yang didukung database:

Buat satu tabel tambahan per target. Misalnya popular_states dan popular_countries , yang merujuk states dan countries masing-masing. Masing-masing tabel "populer" ini juga merujuk ke profil pengguna.

CREATE TABLE popular_states (
  state_id INT NOT NULL,
  user_id  INT NOT NULL,
  PRIMARY KEY(state_id, user_id),
  FOREIGN KEY (state_id) REFERENCES states(state_id),
  FOREIGN KEY (user_id) REFERENCES users(user_id),
);

CREATE TABLE popular_countries (
  country_id INT NOT NULL,
  user_id    INT NOT NULL,
  PRIMARY KEY(country_id, user_id),
  FOREIGN KEY (country_id) REFERENCES countries(country_id),
  FOREIGN KEY (user_id) REFERENCES users(user_id),
);

Ini berarti bahwa untuk mendapatkan semua tempat favorit pengguna yang populer, Anda perlu menanyakan kedua tabel ini. Namun itu berarti Anda dapat mengandalkan database untuk menegakkan konsistensi.

Buat places tabel sebagai supertabel. Seperti yang disebutkan Abie, alternatif kedua adalah tempat populer Anda merujuk ke tabel seperti places , yang merupakan induk dari kedua states dan countries . Artinya, baik negara bagian maupun negara juga memiliki kunci asing ke places (Anda bahkan dapat membuat kunci asing ini juga menjadi kunci utama states dan countries ).

CREATE TABLE popular_areas (
  user_id INT NOT NULL,
  place_id INT NOT NULL,
  PRIMARY KEY (user_id, place_id),
  FOREIGN KEY (place_id) REFERENCES places(place_id)
);

CREATE TABLE states (
  state_id INT NOT NULL PRIMARY KEY,
  FOREIGN KEY (state_id) REFERENCES places(place_id)
);

CREATE TABLE countries (
  country_id INT NOT NULL PRIMARY KEY,
  FOREIGN KEY (country_id) REFERENCES places(place_id)
);

Gunakan dua kolom. Alih-alih satu kolom yang dapat mereferensikan salah satu dari dua tabel target, gunakan dua kolom. Kedua kolom ini mungkin NULL; sebenarnya hanya satu dari mereka yang bukan NULL .

CREATE TABLE popular_areas (
  place_id SERIAL PRIMARY KEY,
  user_id INT NOT NULL,
  state_id INT,
  country_id INT,
  CONSTRAINT UNIQUE (user_id, state_id, country_id), -- UNIQUE permits NULLs
  CONSTRAINT CHECK (state_id IS NOT NULL OR country_id IS NOT NULL),
  FOREIGN KEY (state_id) REFERENCES places(place_id),
  FOREIGN KEY (country_id) REFERENCES places(place_id)
);

Dalam hal teori relasional, Asosiasi Polimorfik melanggar Bentuk Normal Pertama , karena popular_place_id sebenarnya adalah kolom dengan dua arti:itu adalah negara bagian atau negara. Anda tidak akan menyimpan age seseorang dan phone_number mereka dalam satu kolom, dan untuk alasan yang sama Anda tidak boleh menyimpan keduanya state_id dan country_id dalam satu kolom. Fakta bahwa kedua atribut ini memiliki tipe data yang kompatibel adalah kebetulan; mereka masih menandakan entitas logis yang berbeda.

Asosiasi Polimorfik juga melanggar Bentuk Normal Ketiga , karena arti kolom bergantung pada kolom tambahan yang menamai tabel yang dirujuk oleh kunci asing. Dalam Bentuk Normal Ketiga, atribut dalam tabel harus bergantung hanya pada kunci utama tabel itu.

Komentar ulang dari @SavasVedova:

Saya tidak yakin saya mengikuti deskripsi Anda tanpa melihat definisi tabel atau contoh kueri, tetapi sepertinya Anda hanya memiliki beberapa Filters tabel, masing-masing berisi kunci asing yang mereferensikan Products pusat meja.

CREATE TABLE Products (
  product_id INT PRIMARY KEY
);

CREATE TABLE FiltersType1 (
  filter_id INT PRIMARY KEY,
  product_id INT NOT NULL,
  FOREIGN KEY (product_id) REFERENCES Products(product_id)
);

CREATE TABLE FiltersType2 (
  filter_id INT  PRIMARY KEY,
  product_id INT NOT NULL,
  FOREIGN KEY (product_id) REFERENCES Products(product_id)
);

...and other filter tables...

Menggabungkan produk ke jenis filter tertentu itu mudah jika Anda tahu jenis yang ingin Anda gabungkan:

SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)

Jika Anda ingin tipe filter menjadi dinamis, Anda harus menulis kode aplikasi untuk menyusun kueri SQL. SQL mengharuskan tabel ditentukan dan diperbaiki pada saat Anda menulis kueri. Anda tidak dapat membuat tabel gabungan dipilih secara dinamis berdasarkan nilai yang ditemukan di baris individual Products .

Satu-satunya pilihan lain adalah bergabung dengan semua filter tabel menggunakan gabungan luar. Mereka yang tidak memiliki product_id yang cocok hanya akan dikembalikan sebagai satu baris nol. Tetapi Anda masih harus melakukan hardcode semua tabel yang digabungkan, dan jika Anda menambahkan tabel filter baru, Anda harus memperbarui kode Anda.

SELECT * FROM Products
LEFT OUTER JOIN FiltersType1 USING (product_id)
LEFT OUTER JOIN FiltersType2 USING (product_id)
LEFT OUTER JOIN FiltersType3 USING (product_id)
...

Cara lain untuk bergabung ke semua tabel filter adalah dengan melakukannya secara serial:

SELECT * FROM Product
INNER JOIN FiltersType1 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType3 USING (product_id)
...

Tetapi format ini masih mengharuskan Anda untuk menulis referensi ke semua tabel. Tidak ada cara untuk menyiasatinya.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Sintaks SQL DROP DATABASE – Didaftarkan oleh DBMS

  2. Pengecualian:Sudah ada DataReader terbuka yang terkait dengan Koneksi ini yang harus ditutup terlebih dahulu

  3. Bagaimana cara memulai MySQL dengan --skip-grant-tables?

  4. Doctrine 2 dan tabel tautan Banyak-ke-banyak dengan bidang tambahan

  5. Terjadi kesalahan JNI, silakan periksa instalasi Anda dan coba lagi di Eclipse x86 Windows 8.1