简体   繁体   中英

Selecting from tables with many-to-many relationships

I have three tables, one of problems, one of preventive actions, and one of the links between them (problem_pa). Problems may be linked to multiple actions, and preventive actions to mulitple problems, ie a "many-to-many" relationship. I would like an SQL statement that allows me to list all problems that are still in state "open" that do not have any preventive actions in state "open", ie all actions have been performed and closed. I have a search for open problems connected to closed actions (see below):

SELECT p.id, p.slogan, p.state, pa.id, pa.slogan, pa.state
FROM problems p
JOIN problem_pa pp
ON p.id = pp.problem
JOIN preventative_actions pa
ON pa.id = pp.preventative_action
WHERE p.state = "Open" AND pa.state = "Closed"

But this only gets me part of the way as the many-to-many relationship means these problems may still have open actions connected to them as well. How do I get a list of open problems with no remaining open actions (only closed actions)?

Three tables:

problems with the rows (plus other rows not relevant to the question):

id (int(11), AUTO_INCREMENT)
slogan (varchar(255))
state (enum ('Open', 'Closed')

preventative_actions with the rows (plus other rows not relevant):

id (int(11), AUTO_INCREMENT)
slogan (varchar(255))
state (enum ('Open', 'Closed')

problem_pa with the rows:

problem (int(11), to hold the id from the problems table)
preventative_action (int(11), to hold the id from the actions table)

Sample data in problems:

1, Problem number 1, Open
2, Problem number 2, Open
3, Problem number 3, Closed

Sample data in preventative_actions:

1, Action number 1, Open
2, Action number 2, Closed
3, Action number 3, Closed

Sample data in problem_pa

1, 1
1, 2
2, 1
3, 2
3, 3

What I would like my search to return is problem id 3 - it is the only problem that only has links to closed actions. But a search for problems connected to closed actions will return problem ids 1 and 3. What I guess I really want is an inverse of (problems connected to open actions), which would give the inverse of (ids 1 and 2) in the above example.

I have problems connected to up to 60 actions in my database, of which maybe 55 are now closed and 5 are still open. The above is a simplification but I think it contains all relevant data.

This query returns all problems with a status ' Open ' and no opened preventative_actions and at least one closed preventative_actions .

SELECT p.*
FROM problems p
INNER JOIN problem_pa pp ON pp.problem_id = p.id
INNER JOIN preventative_actions pa ON pa.id = pp.preventative_action_id
WHERE p.state = 'Open'
GROUP BY p.id
HAVING SUM(IF(pa.state = 'Open', 1, 0)) = 0 AND SUM(IF(pa.state = 'Closed', 1, 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