简体   繁体   中英

MYSQL: select from many-to-many linking table

I have 3 tables:

1) exercise: exercise_id, name

2) equipment: equipment_id, name

3) exercise_equipment: exercise_id, equipment_id - this is the linking table between exercise and equipment. In this table, one exercise can require multiple pieces of equipment, and the same equipment can be used for many exercises.

CREATE TABLE exercise
    (`ex_id` int, `name` varchar(30))
;   
INSERT INTO exercise
    (`ex_id`, `name`)
VALUES
    (1, 'push ups'),
    (2, 'sit ups'),
    (3, 'squats'),
    (4, 'push ups with a fitness ball')
;

CREATE TABLE equipment
    (`eq_id` int, `name` varchar(30))
;
INSERT INTO equipment
    (`eq_id`, `name`)
VALUES
    (1, 'none'),
    (2, 'mat'),
    (3, 'ball')
;

CREATE TABLE exercise_equipment
    (`ex_id` int, `eq_id` int)
;
INSERT INTO exercise_equipment
    (`ex_id`, `eq_id`)
VALUES
    (1, 2),
    (2, 2),
    (3, 1),
    (4, 2),
    (4, 3)
;

What I need to do is select all the exercises that the user can do with the equipment that he has (for example, 1 and 2 - no equipment or mat).

I have found some examples and tried the following queries with inner join and where in:

SELECT ex.ex_id, ex.name from exercise ex 
LEFT JOIN exercise_equipment exeq ON ex.ex_id = exeq.ex_id 
WHERE exeq.eq_id IN (1,2);

and

select ex_id, name from exercise 
where ex_id in (select ex_id from exercise_equipment where eq_id in (1,2));

But they return all exercises, instead of just the first three. In this case, if the user wants to do exercises that require no equipment or a mat (1 and 2), I want push ups, sit ups, and squats to be returned, but not the mat exercise with a ball.

I hope my explanation is clear. I would really appreciate any help I can get!

You want exercises that use only 1 and 2. When you use a WHERE clause, you are filtering out all the other equipment, so that won't work.

Instead, you need to aggregate and check that no other equipment is being used for the entire exercise. This is a similar query, with a HAVING clause:

SELECT ex.ex_id, ex.name
FROM exercise ex LEFT JOIN
     exercise_equipment exeq
     ON ex.ex_id = exeq.ex_id 
GROUP BY ex.ex_id, ex.name
HAVING SUM(exeq.eq_id NOT IN (1, 2)) = 0;

In the event that some exercises have no equipment at all, you might want:

HAVING COALESCE(SUM(exeq.eq_id NOT IN (1, 2)), 0) = 0;

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