Powoli wyjaśnia się moja sytuacja na uczelni, także mam czas na opisywanie bardziej ambitnych problemów. Dzisiejszy wpis sponsoruje przedmiot Hurtownie i Eksploracja Danych, w ramach którego w tym semestrze naszym zadaniem było m. in. zaprojektowanie rzeczonej hurtowni. Ze względu na to, że uwielbiam przedmioty pozwalające wykorzystać posiadaną wiedzę w praktyce, z przyjemnością zająłem się wykonaniem projektu. ;]

Problem: Nie ma to jak zrozumiałe nazwy błędów.

Strukturę bazy danych obsługującej hurtownię projektowałem w narzędziu MySQL WorkBench, które z tego miejsca szczerze polecam - jak na darmowe oprogramowanie jest naprawdę bardzo solidnym produktem. Po przemyśleniu całego zadania utworzyłem kilkanaście połączonych relacjami tabel i zadowolony z ładnego obrazka [eksport diagramu do pliku PNG jest po prostu genialny] przystąpiłem do pisania sprawozdania.

Po wykonaniu części opisowej przypomniałem sobie, że trzeba jeszcze wstawić kod tworzący całą strukturę [zestaw zapytań CREATE TABLE wraz z odpowiednimi ALTER TABLE dla relacji]. Skonfigurowałem więc połączenie do localhosta w WorkBenchu i kazałem mu zsynchronizować strukturę modelu, aby potem wyeksportować całość poprzez phpMyAdmina. Początkowo nie spojrzałem na listę tabel, jakie zamierza przetworzyć, kliknąłem tylko “next” i po chwili zostałem poinformowany o rzekomym “sukcesie” mojego żądania.

Przywołałem okno Firefoksa, spojrzałem na strukturę bazy i nieco zdziwiony zobaczyłem tylko kilka tabel “liści”, nie posiadających żadnych kluczy obcych. “No cóż, zdarza się” - pomyślałem i spróbowałem stworzyć brakujące tabele ręcznie, kopiując kod SQL z diagramu. Już podczas tworzenia jednej z pierwszych tabel MySQL zaprotestował, twierdząc, że:

#1005 - Can’t create table ‘.[baza][tabela].frm’ (errno: 121)
Nie ma to jak zrozumiałe nazwy błędów, prawda?

Rozwiązanie: Nazwy kluczy obcych muszą być unikalne w skali bazy danych.

Szczerze mówiąc, pierwsze co przyszło mi do głowy, to właśnie sprawdzenie kluczy obcych [muszę się w końcu nauczyć, żeby używać terminu “klucz obcy”, bo zazwyczaj wykorzystuję dosłowne pojęcie “kluczy zagranicznych”, co pozytywnie wpływa na humor rozmówców ;]]. Nie zawsze pierwsze skojarzenie źródła problemu jest poprawne, tym razem jednak się udało. Fragment wygenerowanego kodu jednej z tabel wyglądał następująco:

CREATE  TABLE IF NOT EXISTS `meble`.`produkty` (
  `id` INT NOT NULL AUTO_INCREMENT ,
(...)
  `fabryka` INT NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `FK_Fabryki` (`fabryka` ASC) ,
(...)
  CONSTRAINT `FK_Fabryki`
    FOREIGN KEY (`fabryka` )
    REFERENCES `meble`.`fabryki` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB

Gdzie jest błąd? Otóż, według manuala MySQL, nazwa ograniczenia musi być unikalna w skali całej bazy danych:

If the CONSTRAINT symbol clause is given, the symbol value must be unique in the database. If the clause is not given, InnoDB creates the name automatically.
A więc podczas tworzenia kodu należy wybrać jedną z dwóch możliwości:
  • pozostawić puste nazwy kluczy obcych, dzięki czemu MySQL sam wygeneruje je podczas zapytania tworzącego tabelę i zadba o ich unikalność
  • nazwać klucze unikalnymi nazwami, może to np. być schemat FK[tabela][tabela] - FK_Produkty_Typy
Informacja o tym była mi znana już wcześniej, problem w tym, że MySQL WorkBench nie zaprotestował przy próbie nazwania klucza obcego za pomocą istniejącego już identyfikatora. Cóż, człowiek się uczy całe życie, a szczególnie, kiedy przeoczy pewne oczywistości, tak jak ja w tym przypadku.