简体   繁体   中英

A better solution than using heavily the conditional JOIN

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:

  • IF 'objects.mode' is 0 -> the name of 'object.recipe_id'
  • IF 'objects.mode' is 1
    • IF 'models.family_id > 0' -> the name of 'families_recipes.recipe_id' WHERE distinction_id = foo
    • ELSE -> the name of 'models_recipes.recipe_id' WHERE distinction_id = foo

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM