17

我正在设计一个需要非常灵活的配方数据库,因为它将直接与我们的后台库存系统进行通信。这是我到目前为止关于表格的内容:

  • 食谱:此表将包含食谱日期:名称、烹饪所需的步骤等。
  • 成分/库存:这是我们的内部库存,因此这将包含有关将在我们的食谱中使用的每种产品的信息。
  • 食谱行项目:这是一个棘手的表,我希望能够链接到这里的成分以及食谱所需的数量,但我还需要能够直接包含食谱表中的食谱(例如马力拉酱我们内部制造的),这就是为什么我很难找出设计这张桌子的最佳方法。

基本上,配方行项目表需要能够链接到成分表或配方表,具体取决于需要哪个行项目,我想知道处理它的最有效方法是什么。

非常感谢您!

4

5 回答 5

8

看起来你需要一个类似于这样的数据库模型:

在此处输入图像描述

该模型具有以下属性:

  • 本质上,每个配方都是一系列步骤。
  • 每个步骤都有其相对于同一配方的其他步骤的顺序 (STEP_NO)、单位(质量、体积、计数...)、该单位中的数量等。
  • 特定步骤连接到一种成分(当 INGREDIENT_ID 为非 NULL 时)或连接到另一个配方(当 SUBRECIPE_ID 为非 NULL 时)。1
  • 除此之外,STEP 是一个相当标准的连接表,实现了多对多关系,这意味着相同的成分可以用于多个配方(甚至是同一配方的多个步骤),而且一个配方可以是“子-recipe”的多个其他食谱。
  • 这本质上是一个有向图。数据模型本身不会阻止循环——应该在客户端代码级别避免循环,并可能被触发器检测到。

1如果 MySQL 支持 CHECK 约束(它支持),您可以确保其中一个(但不是两个)是非 NULL 的,如下所示:

CHECK (
    (INGREDIENT_ID IS NULL AND SUBRECIPE_ID IS NOT NULL)
    OR (INGREDIENT_ID IS NOT NULL AND SUBRECIPE_ID IS NULL)
)

就目前而言,您需要一个触发器。

于 2012-12-15T01:59:09.460 回答
3

Ingredients并且Recipes都是可能的RecipeItems

CREATE TABLE RecipeItems (
  ItemID       SERIAL,
  Type         ENUM('Ingredient', 'Recipe'),
  Name         VARCHAR(255) NOT NULL,
  Quantity     FLOAT NOT NULL,
  INDEX (ItemID, Type)
);

CREATE TABLE Ingredients (
  IngredientID BIGINT UNSIGNED NOT NULL,
  Type         ENUM('Ingredient'),
  CostPrice    DECIMAL(6,2),
  PRIMARY KEY (IngredientID),
  FOREIGN KEY (IngredientID, Type) REFERENCES RecipeItems (ItemID, Type)
);

CREATE TABLE Recipes (
  RecipeID     BIGINT UNSIGNED NOT NULL,
  Type         ENUM('Recipe'),
  SellPrice    DECIMAL(6,2),
  Date         DATE,
  Instructions TEXT,
  PRIMARY KEY (RecipeID),
  FOREIGN KEY (RecipeID, Type) REFERENCES RecipeItems (ItemID, Type)
);

然后RecipeLineItems

CREATE TABLE RecipeLineItems (
  RecipeID     BIGINT UNSIGNED NOT NULL,
  ItemID       BIGINT UNSIGNED NOT NULL,
  Quantity     FLOAT NOT NULL,
  PRIMARY KEY (RecipeID, ItemID),
  FOREIGN KEY (RecipeID) REFERENCES Recipes     (RecipeID),
  FOREIGN KEY (ItemID)   REFERENCES RecipeItems (ItemID)
);

使用这种方法,我建议您启用严格的 SQL 模式(否则将在ENUM具有空字符串的类型列中接受无效值''作为特殊错误值):这可能会破坏上述模型的预期引用完整性。另一种(但稍显乏味)的方法是使用触发器手动强制执行参照完整性。

如果只有 MySQL 支持CHECK约束,嗯?

于 2012-12-14T16:56:57.107 回答
1

该脚本将为您创建一个数据库,让您可以管理食谱、成分、食谱成分 (ingredients_recipes) 以及您的库存和成分的统一。它还可以让您管理您的库存历史记录。

这是获取当前食谱、所需成分、所需数量和当前库存的查询:

SELECT recipes.id, recipes.name AS recipeName, ingredients.name AS ingredientNeeded, CONCAT(ingredients_recipes.Qty,' ',neededUnities.name) AS neededQuantity, CONCAT(inventories.qty,' ',inventoryUnities.name) AS availableQuantity FROM recipes 

LEFT JOIN ingredients_recipes ON recipes.id=ingredients_recipes.recipe_id 
LEFT JOIN ingredients ON ingredients_recipes.ingredient_id = ingredients.id 
LEFT JOIN inventories ON ingredients.id=inventories.ingredient_id 
LEFT JOIN unities AS inventoryUnities ON inventories.unity_id=inventoryUnities.id
LEFT JOIN unities AS neededUnities ON ingredients_recipes.unity_id=neededUnities.id

WHERE inventories.`update` = (SELECT MAX(`update`) FROM inventories AS inv WHERE inv.ingredient_id = inventories.ingredient_id);

数据库:

-- --------------------------------------------------------
-- Host:                         127.0.0.1
-- Server version:               5.5.16 - MySQL Community Server (GPL)
-- Server OS:                    Win32
-- HeidiSQL version:             7.0.0.4053
-- Date/time:                    2012-12-14 16:33:22
-- --------------------------------------------------------

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!40014 SET FOREIGN_KEY_CHECKS=0 */;

-- Dumping database structure for database
DROP DATABASE IF EXISTS `database`;
CREATE DATABASE IF NOT EXISTS `database` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE `database`;


-- Dumping structure for table database.ingredients
DROP TABLE IF EXISTS `ingredients`;
CREATE TABLE IF NOT EXISTS `ingredients` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(250) NOT NULL,
  `unity_id` int(11) NOT NULL COMMENT 'for the default unity',
  `Created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `Unity_id` (`unity_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- Dumping data for table database.ingredients: ~0 rows (approximately)
DELETE FROM `ingredients`;
/*!40000 ALTER TABLE `ingredients` DISABLE KEYS */;
/*!40000 ALTER TABLE `ingredients` ENABLE KEYS */;


-- Dumping structure for table database.ingredients_recipes
DROP TABLE IF EXISTS `ingredients_recipes`;
CREATE TABLE IF NOT EXISTS `ingredients_recipes` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `ingredient_id` int(10) NOT NULL,
  `recipe_id` int(10) NOT NULL,
  `Qty` float NOT NULL,
  `Unity_id` int(10) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ingredient_id_recipe_id` (`ingredient_id`,`recipe_id`),
  KEY `Unity_id` (`Unity_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- Dumping data for table database.ingredients_recipes: ~0 rows (approximately)
DELETE FROM `ingredients_recipes`;
/*!40000 ALTER TABLE `ingredients_recipes` DISABLE KEYS */;
/*!40000 ALTER TABLE `ingredients_recipes` ENABLE KEYS */;


-- Dumping structure for table database.inventories
DROP TABLE IF EXISTS `inventories`;
CREATE TABLE IF NOT EXISTS `inventories` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `ingredient_id` int(10) NOT NULL COMMENT 'ingredient',
  `qty` int(10) NOT NULL COMMENT 'quantity',
  `unity_id` int(11) NOT NULL COMMENT 'unity for the ingredient',
  `update` datetime NOT NULL COMMENT 'date of the inventory update',
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- Dumping data for table database.inventories: ~0 rows (approximately)
DELETE FROM `inventories`;
/*!40000 ALTER TABLE `inventories` DISABLE KEYS */;
/*!40000 ALTER TABLE `inventories` ENABLE KEYS */;


-- Dumping structure for table database.recipes
DROP TABLE IF EXISTS `recipes`;
CREATE TABLE IF NOT EXISTS `recipes` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(250) NOT NULL,
  `cooking` longtext NOT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- Dumping data for table database.recipes: ~0 rows (approximately)
DELETE FROM `recipes`;
/*!40000 ALTER TABLE `recipes` DISABLE KEYS */;
/*!40000 ALTER TABLE `recipes` ENABLE KEYS */;


-- Dumping structure for table database.unities
DROP TABLE IF EXISTS `unities`;
CREATE TABLE IF NOT EXISTS `unities` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- Dumping data for table database.unities: ~0 rows (approximately)
DELETE FROM `unities`;
/*!40000 ALTER TABLE `unities` DISABLE KEYS */;
/*!40000 ALTER TABLE `unities` ENABLE KEYS */;
/*!40014 SET FOREIGN_KEY_CHECKS=1 */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
于 2012-12-14T21:34:04.430 回答
0

您将需要三个表:一个recipes表、一个ingredients表和一个recipe_ingredients将成分分配给配方的表。您还可以在此表中存储其他信息,例如数量。因此,例如,如果您有一份蔬菜汤食谱,您将有多个对应数量的蔬菜条目。然后,这些条目将通过外键链接到相关的配方和成分。

编辑:最简单的模式:

CREATE TABLE `ingredients` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`)
) TYPE=InnoDB;

CREATE TABLE `recipes` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`)
) TYPE=InnoDB;

CREATE TABLE `recipe_ingredients` (
  `recipe_id` int(10) unsigned NOT NULL,
  `ingredient_id` int(10) unsigned NOT NULL,
  `quantity` int(10) unsigned NOT NULL,
  KEY `recipe_id` (`recipe_id`),
  KEY `ingredient_id` (`ingredient_id`)
) TYPE=InnoDB;


ALTER TABLE `recipe_ingredients`
  ADD CONSTRAINT `recipe_ingredients_ibfk_2` FOREIGN KEY (`ingredient_id`) REFERENCES `ingredients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  ADD CONSTRAINT `recipe_ingredients_ibfk_1` FOREIGN KEY (`recipe_id`) REFERENCES `recipes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
于 2012-12-14T16:41:37.320 回答
0

单程。

Dish
Key ID Name
1   1  Snails in Marinara Sauce
2   2  Marinara Sauce
3   3  Glass of Water


Ingredient
Key DISHID Name
1   NULL   Snail
2   NULL   Tomato
3   NULL   Onion
4   NULL   Evian
5   2      Marinara Sauce

Recipe
DishID IngredientKey Qty UOM
1      1             6   Each
1      5             3   TblSpoon
2      2             2   Each
2      3             1   Each
3      4             275 Millilitres

因此,如果一种配料是一道菜,它就有一个食谱。

在 OP 提出一个问题后进行了修改,指出了我的潜在答案中的一个小缺陷。

于 2012-12-14T17:05:42.970 回答