2013-04-12 13 views
5

Po raz pierwszy próbuję przechowywać bardziej złożony obiekt w bazie danych. Potrzebuję pomocy przy projektowaniu bazy danych.Jak skonstruować bazę danych za pomocą wielu tabel łączenia

Przedmiot przepis Chcę, aby przechowywać i zregenerować z bazy

{ 
    "id": 2345, 
    "name": "cake", 
    "description": "yummy cake", 
    "categorys": [ 
     17, 
     26 
    ], 
    "persons": 4, 
    "author": 26, 
    "language": "de", 
    "unit": "en", 
    "variantOf": 34, 
    "specialTools": [ 
     34, 
     44, 
     10 
    ], 
    "img": "32598734.jpg", 
    "estTime": 2777, 
    "steps": { 
     "1": { 
      "title": "mix", 
      "description": "mix all together", 
      "img": "45854.jpg", 
      "timer": null, 
      "ingredients": [ 
       { 
        "name": "Butter", 
        "color": "#227799", 
        "amount": 150, 
        "unit": "g" 
       }, 
       { 
        "name": "egg", 
        "color": "#aaff22", 
        "amount": 3, 
        "unit": "pc" 
       }, 
       { 
        "name": "sugar", 
        "color": "#22ffff", 
        "amount": 50, 
        "unit": "g" 
       } 
      ] 
     }, 
     "2": { 
      "title": "bake", 
      "description": "put it in the oven", 
      "img": null, 
      "timer": 2400, 
      "ingredients": [ 
       { 
        "name": "butter", 
        "color": "#227799", 
        "amount": null, 
        "unit": null 
       }, 
       { 
        "name": "sugar", 
        "color": "#22ffff", 
        "amount": null, 
        "unit": null 
       }, 
       { 
        "name": "egg", 
        "color": "#aaff22", 
        "amount": null, 
        "unit": null 
       } 
      ] 
     } 
    } 
} 

Najbardziej skomplikowana część jest przedmiotem steps. Każda receptura może mieć różną liczbę kroków z różnymi składnikami przypisanymi do każdego zestawu.

Oto projekt bazie zrobiłem database scheme

recipe_id, step_id są klucze obce. Chcę wszystko w różnych tabelach, ponieważ przepisy powinny być sortable przez składników, Kategorie ...

kod SQL generowania najważniejszych tabel

-- ----------------------------------------------------- 

-- Table `dev_Recipe`.`recipe` 

-- ----------------------------------------------------- 

CREATE TABLE IF NOT EXISTS `dev_Recipe`.`recipe` (

    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , 

    `name` VARCHAR(255) NULL , 

    `description` TEXT NULL , 

    `author_id` INT UNSIGNED NOT NULL , 

    PRIMARY KEY (`id`) , 

    INDEX `author_id_idx` (`author_id` ASC) , 

    CONSTRAINT `author_id` 

    FOREIGN KEY (`author_id`) 

    REFERENCES `dev_Recipe`.`users` (`id`) 

    ON DELETE NO ACTION 

    ON UPDATE NO ACTION) 

ENGINE = InnoDB; 



-- ----------------------------------------------------- 

-- Table `dev_Recipe`.`step` 

-- ----------------------------------------------------- 

CREATE TABLE IF NOT EXISTS `dev_Recipe`.`step` (

    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , 

    `recipe_id` INT UNSIGNED NOT NULL , 

    `step_number` INT UNSIGNED NOT NULL , 

    `description` TEXT NULL , 

    `timer` INT UNSIGNED NULL , 

    `image` VARCHAR(100) NULL , 

    PRIMARY KEY (`id`) , 

    INDEX `recipe_id_idx` (`recipe_id` ASC) , 

    CONSTRAINT `step_recipe_id` 

    FOREIGN KEY (`recipe_id`) 

    REFERENCES `dev_Recipe`.`recipe` (`id`) 

    ON DELETE NO ACTION 

    ON UPDATE NO ACTION) 

ENGINE = InnoDB; 


-- ----------------------------------------------------- 

-- Table `dev_Recipe`.`ingredient` 

-- ----------------------------------------------------- 

CREATE TABLE IF NOT EXISTS `dev_Recipe`.`ingredient` (

    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , 

    `name` VARCHAR(45) NOT NULL , 

    `color` INT NOT NULL , 

    `img` VARCHAR(45) NULL , 

    PRIMARY KEY (`id`)) 

ENGINE = InnoDB; 


-- ----------------------------------------------------- 

-- Table `dev_Recipe`.`step_ingredients` 

-- ----------------------------------------------------- 

CREATE TABLE IF NOT EXISTS `dev_Recipe`.`step_ingredients` (

    `recipe_id` INT UNSIGNED NOT NULL , 

    `ingredient_id` INT UNSIGNED NOT NULL , 

    `step_id` INT UNSIGNED NOT NULL , 

    `amount` INT NULL , 

    `unit` VARCHAR(25) NULL , 

    INDEX `recipe_id_idx` (`recipe_id` ASC) , 

    INDEX `ingredient_id_idx` (`ingredient_id` ASC) , 

    INDEX `step_id_idx` (`step_id` ASC) , 

    PRIMARY KEY (`recipe_id`, `step_id`) , 

    CONSTRAINT `step_ing_recipe_id` 

    FOREIGN KEY (`recipe_id`) 

    REFERENCES `dev_Recipe`.`recipe` (`id`) 

    ON DELETE NO ACTION 

    ON UPDATE NO ACTION, 

    CONSTRAINT `ingredient_step_ing_id` 

    FOREIGN KEY (`ingredient_id`) 

    REFERENCES `dev_Recipe`.`ingredient` (`id`) 

    ON DELETE NO ACTION 

    ON UPDATE NO ACTION, 

    CONSTRAINT `step_ing_id` 

    FOREIGN KEY (`step_id`) 

    REFERENCES `dev_Recipe`.`step` (`id`) 

    ON DELETE NO ACTION 

    ON UPDATE NO ACTION) 

ENGINE = InnoDB;

Ponieważ nigdy wcześniej łączenia tabel, Nie wiem, czy to właściwe podejście do mojego problemu. Czy jest to projekt reasonalbe i jak go zoptymalizować?

Zrobiłem inny projekt, gdzie recipes jest połączony z step i step z ingredients. Myślę, że pierwszy układ jest łatwiejszy do wyszukania, ponieważ mogę wyszukiwać pod ingredients_idrecipe_id, patrząc tylko na step_ingredients, ale nie jestem pewien. jakieś pomysły?

database design 2

Odpowiedz

5

Najważniejsze z relacyjnych baz danych jest to, że istnieją 3 rodzaje relacji FK:

  • 1 do 1
  • 1 do wielu
  • wiele do wielu

To jest powiedział, twój schemat wygląda dobrze znormalizowany i logiczny na pierwszy rzut oka. Jedyną ostrożnością, którą chciałbym umieścić, jest to, że rekursja może być trudna w SQL dla kategorii z własnymi referencjami.

Kilka uwag:

Krokiem składnik wymaga etapu, który ma już recipe_id (ewentualnie null)

Może to krok składnik istnieć bez etapu

Krokiem może istnieć bez przepis

użytkownika do receptury jest jeden do jednego (jak wspomniano) Dan

Edycja: W trosce o podwójne łączenie zamiast pojedynczego sprzężenia, aby przejść od receptury do składnika, tutaj jest problem normalizacji, który jest trudny z oryginalnym projektem: co sprawia, że ​​step_ingredient i step recipe_id jest taki sam? W tej chwili nie byłoby gwarancji spójności. Jeśli zastanawiasz się nad projektem danych, to naprawdę myślisz, że dołączysz do tych dwóch stołów, więc dlaczego nie połączyć ich z niepotrzebnym FK (nie rób tego albo rzeczy będą się dziurawić szybko))

Twój Drugi projekt w rzeczywistości pozwala również na taką samą liczbę złączeń, ponieważ uwzględniłeś parametr recipe_id jako PK w tabeli kroków, który następnie staje się PK/FK w step_ingredient i gwarantuje konsystencję recipe_id. ex:

SELECT ingredient_id 
FROM Recipe r 
JOIN Step_ingredient si on si.step_recipe_id = r.recipe_id 
JOIN Ingredient i on si.ingredient_id = i.ingredient_id 

i mój ulubiony Link do rozpoczęcia pracy z normalizacji bazy danych: http://en.wikipedia.org/wiki/Database_normalization

+0

Dziękuję za odpowiedź. Czy możesz zaproponować lepszy sposób organizowania podkategorii? Czy byłoby lepiej ograniczyć poziom podkategorii i utworzyć tabelę dla każdej warstwy? Nie bardzo rozumiem, co chcesz powiedzieć, "Czy składnik krok może istnieć bez kroku". "Składnik krokowy" nie powinien istnieć bez "kroku". A "krok" powinien należeć do przepisu. – Akkumulator

+1

W takim przypadku składnik krokowy nie powinien wymagać połączenia bezpośrednio z recepturą, a krok powinien wymagać FK do przepisu. Podkategorie mogą być trudne w sql, istnieje wiele różnych sposobów organizowania struktur podobnych do drzewa http://stackoverflow.com/questions/2175882/how-to-representa-a-data-tree-in-sql. Jeśli znasz głębokość kategoryzacji, łatwiej będzie utworzyć wyrównane tabele. – munch1324

+0

Czy myślisz, że moja druga diagrama, dodana później, byłaby lepsza? – Akkumulator

2

Na pierwszy rzut oka wygląda bardzo dobrze. Jednak nie jestem pewien, dlaczego tabele kategorii i składników muszą mieć identyfikatory nadrzędne.

Potrzebne są również wiele do wielu relacji między użytkownikami i przepisami. Obecnie masz to, co wygląda jeden na jeden.

+0

identyfikatory macierzyste są tam subcategorys. Jeśli kategoria odwołuje się do innej kategorii, jest jej podkategorią. jeśli "parent_id" jest równe 'Null' jest główną kategorią. Nie opracowałem jeszcze obsługi użytkownika. Ale dzięki za wskazanie tego :) – Akkumulator

+1

Zamiast niwelowania identyfikatora nadrzędnego dla głównych kategorii, dałbym mu tę samą wartość, co identyfikator kategorii. Wartości Null w polach klucza obcego mogą być dozwolone w niektórych bazach danych, ale pocierają mnie w niewłaściwy sposób. –

Powiązane problemy