Kaip ištaisyti „a foreign key constraint fails“ importuojant SQL išklotinę

Importas nutrūksta įpusėjęs su pranešimu Cannot add or update a child row: a foreign key constraint fails. Duomenys tvarkingi — neteisinga yra INSERT sakinių tvarka. Štai kodėl taip nutinka ir trys būdai, kaip tai ištaisyti.

Eksportuojate duomenų bazę, bandote ją įkelti į naują serverį, o MySQL išmeta:

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`app`.`orders`, CONSTRAINT `orders_user_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`))

Arba PostgreSQL atveju:

ERROR: insert or update on table "orders" violates foreign key constraint "orders_user_id_fkey"
DETAIL: Key (user_id)=(42) is not present in table "users".

Kodėl taip nutinka

Išorinis raktas sako: „kiekvienas orders.user_id turi rodyti į egzistuojantį users.id.“ Kai išklotinė įterpia orders anksčiau nei users, nurodyta pirminė eilutė dar neegzistuoja, ir duomenų bazė atmeta eilutę.

Dauguma išklotinių įrankių lenteles rašo abėcėlės tvarka arba sukūrimo tvarka — bet ne pagal išorinių raktų priklausomybių tvarką. Todėl orders (antrinė) dažnai atsiduria prieš users (pirminę), ir importas nutrūksta.

1 sprendimas: pertvarkyti INSERT pagal priklausomybes (teisingas sprendimas)

Švarus sprendimas — kiekvieną pirminę lentelę įterpti prieš jos antrines lenteles. Tai reiškia išorinius raktus paversti priklausomybių grafu ir lenteles įkelti topologine tvarka: users → orders → order_items.

Daryti tai rankomis kelių gigabaitų faile — kančia: tenka atsekti kiekvieną FOREIGN KEY per CREATE TABLE ir ALTER TABLE sakinius bei iškirpti ir įklijuoti didžiulius INSERT blokus teisinga tvarka.

2 sprendimas: išjungti išorinių raktų tikrinimą importo metu

Jei išklotinė yra pilna ir jau nuosekli, tikrinimą galima saugiai praleisti įkėlimo metu. MySQL atveju apgaubkite failą:

-- MySQL
SET FOREIGN_KEY_CHECKS = 0;
-- ... visi jūsų INSERT sakiniai ...
SET FOREIGN_KEY_CHECKS = 1;

PostgreSQL atveju atidėkite apribojimus tranzakcijos viduje:

-- PostgreSQL
BEGIN;
SET CONSTRAINTS ALL DEFERRED;
-- ... visi jūsų INSERT sakiniai ...
COMMIT;

Po to vėl įjunkite tikrinimą, kad būsimi įrašymai liktų patikrinti. Tai veikia, tačiau tik paslepia eiliškumo problemą — jei išklotinė yra dalinė, vis tiek galite likti su „našlaitėmis“ eilutėmis.

3 sprendimas: padaryti abu automatiškai su DumpCleaner

DumpCleaner perskaito išklotinę, iš CREATE TABLE ir ALTER TABLE ištraukia kiekvieną išorinį raktą, sudaro priklausomybių grafą ir eksportuoja švarų failą, kuris tiesiog importuojasi:

  1. Nutempkite savo .sql išklotinę į DumpCleaner (MySQL, MariaDB, PostgreSQL, SQLite ar MS SQL).
  2. Pasirinkite INSERT tvarka → Pagal priklausomybes (atsižvelgiant į FK). Pirminės lentelės automatiškai išdėstomos prieš antrines, naudojant topologinį rūšiavimą.
  3. Norėdami, pažymėkite Išjungti išorinių raktų tikrinimą — tuomet išvestis apgaubiama teisingu jūsų duomenų bazės sakiniu.
  4. Eksportuokite. Importuokite naują failą — apribojimo klaidos nebėra.

Programa netgi aptinka ciklinius išorinius raktus ir įspėja jus, užuot sukūrusi neįmanomą tvarką.

Jokios daugiau rankinės INSERT „chirurgijos“

DumpCleaner pertvarko INSERT pagal išorinių raktų priklausomybes bet kokio dydžio failuose — srautiniu būdu, su pastoviu atminties naudojimu. Vietinė macOS ir iPadOS programa, vienkartinis pirkimas.

Atsisiųsti iš App Store

Dažnai užduodami klausimai

Kodėl importuojant įvyksta „a foreign key constraint fails“?

Antrinė eilutė nurodo pirminę eilutę, kuri dar nebuvo įterpta, nes išklotinė INSERT sakinius išdėsto abėcėlės ar sukūrimo tvarka, o ne pagal išorinių raktų priklausomybes.

Ar saugu išjungti išorinių raktų tikrinimą importo metu?

Taip, jei išklotinė pilna ir nuosekli. Jūs tik praleidžiate duomenų, kurie šaltinio duomenų bazėje jau buvo teisingi, tikrinimą. Po to vėl įjunkite tikrinimą.

Kaip pertvarkyti INSERT nekeičiant failo rankomis?

DumpCleaner aptinka išorinius raktus, sudaro priklausomybių grafą ir eksportuoja INSERT topologine tvarka — kad pirminės lentelės visada įsikeltų prieš antrines.

Ar tai veikia ir su PostgreSQL?

Taip. DumpCleaner palaiko pg_dump išvestį ir gali sugeneruoti tiek SET CONSTRAINTS ALL DEFERRED, tiek pagal priklausomybes išdėstytus įterpimus.

Importuokite išklotines be FK galvos skausmo

Nutempkite, pertvarkykite pagal priklausomybes, eksportuokite. DumpCleaner sutvarko išorinius raktus, kad jūsų importas tiesiog veiktų.

Atsisiųsti iš App Store