Tidak dapat mengubah kolom yang digunakan dalam batasan kunci asing

111

Saya mendapatkan kesalahan ini saat mencoba mengubah tabel saya.

Error Code: 1833. Cannot change column 'person_id': used in a foreign key constraint 'fk_fav_food_person_id' of table 'table.favorite_food'

Berikut adalah PERNYATAAN CREATE TABLE saya Yang berjalan dengan sukses.

CREATE TABLE favorite_food(
    person_id SMALLINT UNSIGNED,
    food VARCHAR(20),
    CONSTRAINT pk_favorite_food PRIMARY KEY(person_id,food),
    CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
    REFERENCES person (person_id)
);

Kemudian saya mencoba menjalankan pernyataan ini dan saya mendapat kesalahan di atas.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;
theJava
sumber
4
Contoh di atas adalah dari buku "Learning SQL, edisi ke-2". Saya berharap penulis, Alan Beaulieu membuat koreksi.
Dmitry

Jawaban:

126

Jenis dan definisi kolom kunci asing dan referensi harus sama. Ini berarti kunci asing Anda tidak mengizinkan perubahan jenis bidang Anda.

Salah satu solusinya adalah ini:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

ALTER TABLE favorite_food
    DROP FOREIGN KEY fk_fav_food_person_id,
    MODIFY person_id SMALLINT UNSIGNED;

Sekarang Anda dapat mengubah Anda person_id

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

membuat ulang kunci asing

ALTER TABLE favorite_food
    ADD CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
          REFERENCES person (person_id);

UNLOCK TABLES;

EDIT: Menambahkan kunci di atas, berkat komentar

Anda harus melarang penulisan ke database saat Anda melakukan ini, jika tidak, Anda berisiko mengalami masalah integritas data.

Saya telah menambahkan kunci tulis di atas

Semua pertanyaan penulisan di sesi lain selain sesi Anda sendiri ( INSERT, UPDATE, DELETE) akan menunggu hingga batas waktu atau UNLOCK TABLES; dieksekusi

http://dev.mysql.com/doc/refman/5.5/en/lock-tables.html

EDIT 2: OP meminta penjelasan yang lebih rinci tentang baris "Jenis dan definisi kolom kunci asing dan referensi harus sama. Ini berarti kunci asing Anda tidak mengizinkan perubahan jenis kolom Anda."

Dari Manual Referensi MySQL 5.5: Batasan KUNCI ASING

Kolom yang sesuai di foreign key dan referensi key harus memiliki tipe data internal yang sama di dalam InnoDB sehingga dapat dibandingkan tanpa konversi jenis. Ukuran dan tanda tipe integer harus sama. Panjang jenis string tidak harus sama. Untuk kolom string non-biner (karakter), kumpulan karakter dan pemeriksaan harus sama.

Michel Feldheim
sumber
1
Jangan lupa untuk menggunakan transaksi untuk ini. Jika tidak, database Anda mungkin rusak.
Francois Bourgeois
5
Poin bagus, sayangnya MySQL tidak mendukung transaksi seputar pernyataan DDL. Transaksi terbuka dilakukan sebelum permintaan DDL dieksekusi lihat dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim
2
Pernyataan yang benar untuk membuat ulang kunci asing adalah: ALTER TALE favorite_food ADD CONTRAINT fk_fav_food_person_id FOREIGN KEY (person_id) REFERENSI orang (id);
Felizardo
1
Mengapa Anda memodifikasi person_idsetelah melepaskan kunci asing? Sepertinya Anda tidak mengubah apa pun karena sudah menjadi SMALLINT UNSIGNED.
Dennis Subachev
1
Kami tidak tahu apa itu karena dia hanya memposting struktur tabel referensi. Innodb memiliki int sebagai tipe internal, smallint dll hanya pintasan
Michel Feldheim
198

Anda dapat mematikan pemeriksaan kunci asing:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Harap pastikan untuk TIDAK menggunakan ini pada produksi dan memiliki cadangan.

Dementik
sumber
1
Tampaknya solusi yang sangat tidak aman. Apakah ini berpotensi menyebabkan hilangnya integritas data?
hrust
@Synaps - ya bisa saja jika Anda menghapus / memperbarui / menyisipkan. kehilangan data tidak akan terjadi jika Anda hanya memodifikasi tabel atau melakukan seeding db Anda, di sisi lain Anda harus memvalidasi data Anda secara manual (karena Anda menghilangkan batasan)
Dementic
2
Solusi ini hebat, dan Anda dapat menggunakannya pada produksi jika Anda mengiklankan kunci tulis sebelum mengubah data Anda dan kemudian membuka kuncinya setelah selesai. Menggunakan file sql untuk melakukan perubahan Anda dalam waktu sesingkat mungkin akan lebih baik.
Francisco Zarabozo
Solusi bagus untuk penyesuaian cepat
Genaut
1
Anda tidak memerlukan kunci seperti SET FOREIGN_KEY_CHECKScakupan sesi (sesi lain masih akan menerapkan batasan FK). Itu sempurna untuk menambahkan / menghapus AUTO_INCREMENT(yang tidak berubah yang sebenarnya kolom datatype), tetapi tidak akan bekerja jika Anda mencoba untuk mengubah kolom datatype untuk "nyata" (katakanlah, dari SMALLINT ke INT) karena Anda akan mendapatkan yang sah 150 FK constraint incorrectly formedsaat mysql mencoba mengganti tabel lama dengan yang baru. Dalam kasus seperti itu, gunakan jawaban yang diterima.
Xenos
-3

Ketika Anda mengatur kunci (primer atau asing) Anda menetapkan batasan tentang bagaimana mereka dapat digunakan, yang pada gilirannya membatasi apa yang dapat Anda lakukan dengan mereka. Jika Anda benar-benar ingin mengubah kolom, Anda dapat membuat ulang tabel tanpa batasan, meskipun saya merekomendasikan untuk tidak melakukannya. Secara umum, jika Anda memiliki situasi di mana Anda ingin melakukan sesuatu, tetapi terhalang oleh suatu kendala, paling baik diselesaikan dengan mengubah apa yang ingin Anda lakukan daripada dengan kendala.

RonaldBarzell
sumber
12
Ini adalah TERSEBUT merupakan tidak membantu, jawabannya tidak berguna!
ajmedway
3
@ajmedway Maka Anda dapat menulis jawaban yang membantu tanpa menyalahkan pengguna lain
Saya Orang Paling Bodoh
2
@IamtheMostStupidPerson Jauh lebih baik daripada hanya downvoting tanpa komentar. Setidaknya pemberi komentar bisa menebak mengapa suara negatif tersebut. Jawaban umum seperti "lebih baik aman daripada menyesal" tidak berguna.
Csaba Toth