I'm trying to create a "complex" view in MySql. I need good performance because I have to query it 2 times per second and each result count about 1200 rows.
I report a schema example with data:
CREATE TABLE objects (
object_id INT AUTO_INCREMENT,
model_id INT,
mode TINYINT,
recipe_id INT,
CONSTRAINT pk_objects PRIMARY KEY (object_id));
INSERT INTO objects (model_id, mode, recipe_id) VALUES (1, 0, 1), (1, 1, 1), (2, 1, 1);
CREATE TABLE models (
model_id INT AUTO_INCREMENT,
family_id INT,
CONSTRAINT pk_models PRIMARY KEY (model_id));
INSERT INTO models (family_id) VALUES (0), (1);
CREATE TABLE models_recipes (
model_id INT,
recipe_id INT,
distinction_id INT,
CONSTRAINT pk_models_recipes PRIMARY KEY (model_id, recipe_id, distinction_id));
INSERT INTO models_recipes (model_id, recipe_id, distinction_id) VALUES (1, 2, 1), (1, 3, 2);
CREATE TABLE families (
family_id INT AUTO_INCREMENT,
name VARCHAR(45),
CONSTRAINT pk_families PRIMARY KEY (family_id));
INSERT INTO families (name) VALUES ("Family_1");
CREATE TABLE families_recipes (
family_id INT,
recipe_id INT,
distinction_id INT,
CONSTRAINT pk_families_recipes PRIMARY KEY (family_id, recipe_id, distinction_id));
INSERT INTO families_recipes (family_id, recipe_id, distinction_id) VALUES (1, 3, 1), (1, 2, 2);
CREATE TABLE recipes (
recipe_id INT AUTO_INCREMENT,
name VARCHAR(45),
CONSTRAINT pk_recipes PRIMARY KEY (recipe_id));
INSERT INTO recipes (name) VALUES ("recipe1"), ("recipe2"), ("recipe3");
My view needs to report the recipe name in these different conditions:
I have written this query:
SELECT o.object_id, o.mode, o.model_id,
CASE
WHEN o.mode = 1 THEN
CASE
WHEN m.family_id > 0 THEN rf.name
ELSE rm.name
END
WHEN o.mode = 0 THEN ro.name
END AS 'recipe_name'
FROM objects AS o
LEFT JOIN models AS m
ON o.model_id = m.model_id
LEFT JOIN (SELECT * FROM models_recipes WHERE distinction_id = 1) AS mr
ON m.model_id = mr.model_id
LEFT JOIN recipes AS rm
ON mr.recipe_id = rm.recipe_id
LEFT JOIN (SELECT * FROM families_recipes WHERE distinction_id = 1) AS fr
ON m.family_id = fr.family_id
LEFT JOIN recipes AS rf
ON fr.recipe_id = rf.recipe_id
LEFT JOIN recipes AS ro
ON o.recipe_id = ro.recipe_id;
and the result is right
object_id | mode | model_id | recipe_name
-----------------------------------------
1 | 0 | 1 | recipe1
2 | 1 | 1 | recipe2
3 | 1 | 2 | recipe3
But I'm looking for a better solution, avoiding to JOIN the wanted data (recipes) a number of times equal to the number conditions. Thanks
You can join recipes
only once if you use conditional aggregation:
select o.object_id, o.mode, o.model_id,
case o.mode
when 0 then max(case when r.recipe_id = o.recipe_id then r.name end)
when 1 then case
when m.family_id > 0 then max(case when r.recipe_id = fr.recipe_id then r.name end)
else max(case when r.recipe_id = mr.recipe_id then r.name end)
end
end recipe_name
from objects o
left join models m on m.model_id = o.model_id
left join families f on f.family_id = m.family_id
left join families_recipes fr on fr.family_id = f.family_id and fr.distinction_id = 1
left join models_recipes mr on mr.model_id = m.model_id and mr.distinction_id = 1
left join recipes r on r.recipe_id in (o.recipe_id, fr.recipe_id, mr.recipe_id)
group by o.object_id, o.mode, o.model_id
See the demo .
Results:
object_id | mode | model_id | recipe_name
--------: | ---: | -------: | :----------
1 | 0 | 1 | recipe1
2 | 1 | 1 | recipe2
3 | 1 | 2 | recipe3
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.