I'm scratching my head, looking for a reasonable way of achieving this. The title was really difficult to figure out, so I'm sorry if you entered this post hoping for something else. It's both a database and a logic question.
I have 100 entries in a "collection". Let's call each one an object. Each object refers to an entry in a database, and has some name (let's say obj1, obj2 ...). Some of these objects are a "compound" of other objects. So, for instance, if obj1 and obj2 are inputs, obj11 is the output (obj1, obj2 -> obj11). Maybe even multiple inputs of the same object: obj1 x 4, obj2, obj11 -> obj21.
I'm looking for a way to calculate the resulting object continuously while changing the input, and thus these relations must have some clever way of being stored in a database, so that a calculation like this can be done in a rather elegant fashion.
Naturally I thought of a many-to-one table, but couldn't find a logical way to then find a result for given inputs without parsing the whole table.
Does anyone have some thoughts on this?
You seem to be building a sort of recipe system. Some objects are created from different quantities of other objects. In terms of datastructure, you could have something like
struct Thing {
std::vector<Ingredient> parts;
// ... other attributes here
};
struct Ingredient {
Thing component;
int count;
};
This can be stored using
CREATE TABLE `Things` (
`id` int(11) NOT NULL AUTO_INCREMENT,
# ... other attributes here
PRIMARY KEY `id`
);
CREATE TABLE `Ingredients` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`result_id` int(11) NOT NULL,
`component_id` int(11) NOT NULL,
`count` int(11) NOT NULL,
PRIMARY KEY `id`
);
ALTER TABLE `Ingredients` FOREIGN KEY (`result_id`) REFERENCES `Thing` (`id`);
ALTER TABLE `Ingredients` FOREIGN KEY (`component_id`) REFERENCES `Thing` (`id`);
To store the recipe for a Thing
, just insert all its components and their quantities as ingredients. To retrieve the recipe, just SELECT
all ingredients with that result_id
. To see where a Thing
is used in a recipe, just query for its use as a component.
Imagine that A
is built from B
and C
; and that B
can be built from D
and E
. Then you may want to perform a multi-stage query, to figure out from A
= B
+ C
that A
= ( D
+ E
) + C
. This requires one extra select on each component, to see if it has, itself, ingredients. However, this will backfire unless your recipes form a tree: what if A
requires B
and B
can be built from A
?. Even worse: what if there are several means to obtain A
, say from B
+ C
or from X
+ Y
? If you need to distinguish among these alternative recipes, you would need an additional table:
struct Thing {
std::vector<Recipe> recipes;
// ... other attributes here
};
struct Recipe {
std::vector<Ingredient> ingredients;
// ... other attributes here
};
struct Ingredient {
Thing component;
int count;
};
And, in SQL,
CREATE TABLE `Recipes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`result_id` int(11) NOT NULL,
PRIMARY KEY `id`
);
CREATE TABLE `Ingredients` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`recipe_id` int(11) NOT NULL,
`component_id` int(11) NOT NULL,
`count` int(11) NOT NULL,
PRIMARY KEY `id`
);
ALTER TABLE `Recipes` FOREIGN KEY (`result_id`) REFERENCES `Thing` (`id`);
ALTER TABLE `Ingredients` FOREIGN KEY (`recipe_id`) REFERENCES `Recipe` (`id`);
ALTER TABLE `Ingredients` FOREIGN KEY (`component_id`) REFERENCES `Thing` (`id`);
If you need to recover multi-stage recipes on something like this, then you are trying to perform graph queries on a relational database, and may find it a lot easier to use an object-graph database that is intended for just those types of queries.
Edit: assuming no cycles, and at most 1 recipe per Thing
, how do I find what to cook given a list of ingredients? Several approaches are possible.
SELECT possible.id
FROM (
SELECT i.result_id AS id, COUNT(*) AS total
FROM `Ingredients` AS i GROUP BY i.result_id
) AS possible, (
SELECT result_id, COUNT(result_id) AS total
FROM `Ingredients` AS i WHERE
(i.component_id = 1 AND i.count<=1) OR
(i.component_id = 2 AND i.count<=3) OR
(i.component_id = 42 AND i.count<=1)
GROUP BY i.result_id
) AS valid
WHERE possible.id = valid.result_id AND possible.total = valid.total;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.