I'm trying to create an effective query but can't get it working.
Tables:
- one table containing types of objects
- one table containing objects
Conditions:
- there can be single objects of a type
- there can be child objects of a type
- parent and child objects don't need to be of the same type
- objects can be published
- types can be published
- the results should only get pulled from a specific pool of object IDs. So i need to add AND (o.id IN (1,2,3,4)
I want a simple result list that shows how many types are published and the number of objects assigned to these types.
types
id | title | published
---------------------
1 type1 1
2 type2 1
3 type3 1
4 type4 1
5 type5 1
6 type6 0
7 type7 1
objects
id |title | type | parent | published
---------------------------------------
1 a 1 0 1
2 b 1 0 1
3 c 3 2 1
4 d 2 0 1
5 e 2 2 1
6 f 4 0 0
7 g 5 6 1
8 h 6 0 1
9 i 3 8 1
10 j 3 8 0
11 k 7 8 1
Results should be:
type1 (#2) (two singles)
type2 (#2) (one single + one child of id 2)
type3 (#3) (one child of id 2 + one published child of id 8)
type4 (#0) (one single not published)
type5 (#0) (because it's parent id 6 is not published)
type6 (#0) (because type6 is not published)
I tried this one (type publishing not included):
SELECT o.type, t.title, COUNT(t.id) AS cnt
FROM types AS t
LEFT JOIN objects AS o ON o.type = t.id
LEFT JOIN objects AS o2 ON o.id = o2.parent
WHERE o.published = 1 AND o2.published = 1
GROUP BY o.type
The conditions in the WHERE clause negate the "outerness" of the left joins.
Move those conditions to the ON clauses. The WHERE clause can be dropped.
Also, reference columns from t, the driving table, and count non-NULL expressions from the outer joined tables.
That will allow the query to return zero counts.
I didn't fully delve into the specification, but it looks like we want to count matching rows from o and o2.
I think something like this will get a resultset consistent with one interpretation of the specification... child o2 rows get counted under parent o type, regardless of the type on the child o2 row.
This is not tested, and I'm not fully understanding the specification...
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(DISTINCT o.id)
+ COUNT(DISTINCT o2.id) AS `cnt`
-- , COUNT(DISTINCT o.id) AS `cnt_o`
-- , COUNT(DISTINCT o2.id) AS `cnt_o2`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND o.parent = 0
AND t.published = 1
LEFT
JOIN objects o2
ON o2.parent = o.id
AND o2.published = 1
GROUP
BY t.id
, t.title
Not clear in the spec...
Do child rows (from o2) get omitted from the count if the type on the o2 row matches a row in types that is published=0 ?
If we are "grouping" by type on the o2 rows , then we'd need to something different,
EDIT
we could get the count from the parent and the child separately, in two separate SELECT, and then combine the two resultsets with a UNION ALL set operator, and then total up the counts.
something along these lines:
SELECT c.type
, c.title
, SUM(c.cnt) AS cnt
FROM (
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(o.id) AS `cnt`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND o.parent = 0
AND t.published = 1
GROUP
BY t.id
, t.title
UNION ALL
SELECT tc.id AS `type`
, tc.title AS `title`
, COUNT(oc.id) AS `cnt`
FROM types tc
JOIN objects oc
ON oc.type = t.id
AND oc.published = 1
AND t.published = 1
JOIN objects op
ON op.id = oc.parent
AND op.published = 1
JOIN types pt
ON pt.id = op.type
AND pt.published = 1
GROUP
BY tc.id
, tc.title
) c
GROUP
BY c.type
, c.title
again, untested, and without a full understanding of the spec.
the count of the parent o is straightforward. we use an outer join, with t as the driving table, so we get all types, and can get zero counts.
the count of the child oc, we can do inner joins. since the previous SELECT is getting us all the types, missing rows in the second SELECT won't cause a problem.
note that we join the child o2 rows by type, and then we join to parent (to make sure parent is published), and join to parent type (to check that type is published) ...
How do we distinguish "parent" rows, do we check parent=0 ?
Is this a hierarchy, can a "child" also be the "parent" of another row ?
FOLLOWUP
Another way to think about it (maybe this was the approach of the OP query) ... we are counting rows from o, parents and children. What's important is that the type is published type, and that o is published.
Additionally, either
o is not a child (i.e. there isn't a row in objects op that has an id value equal to `o.parent)
or
if o does have a parent row (a row in objects op with an id value equal to o.parent, the [parent op is published and the parent type is published.
We could approach it like this:
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(o.id) AS `cnt`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND t.published = 1
LEFT
JOIN objects op
ON op.id = o.parent
LEFT
JOIN types pt
ON pt.id = op.type
WHERE -- this not a child (there is no parent)
op.id IS NULL
OR -- parent is published and parent type is published
( op.published = 1 AND pt.published = 1 )
GROUP
BY t.id
, t.title
Related
I have seen a number of similar questions posted to StackOverflow and I haven't been successful in my attempts to apply them.
I would like to take an existing working COUNT(*) query and update it such that it also returns the rows with zero as the count value. I think that I need to use a LEFT JOIN but I'm not sure how to get it to work.
My current working query:
SELECT `VIC_HELMET_BELT`.`id`, `VIC_HELMET_BELT`.`HELMET_BELT` AS 'desc', COUNT(*) AS 'count' FROM `VIC_ACCIDENT`, `VIC_NODE`, `VIC_PERSON`, `VIC_VEHICLE`, `VIC_HELMET_BELT`
WHERE `VIC_NODE`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
AND `VIC_PERSON`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
AND `VIC_VEHICLE`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
AND `VIC_PERSON`.`SEATING_POSITION_Id` = 1
AND `VIC_HELMET_BELT`.`id` = `VIC_PERSON`.`HELMET_BELT_WORN`
GROUP BY `VIC_HELMET_BELT`.`id`
This returns the table:
id desc count
------ ----------------------------- --------
1 Seatbelt worn 390115
2 Seatbelt not worn 10158
4 Child restraint not worn 1
5 Seatbelt/restraint not fitted 1573
6 Helmet worn 60521
7 Helmet not worn 3495
8 Not appropriate 3635
9 Not known 168617
I would like the table to be returned with id=3 with a count of 0:
id desc count
------ ----------------------------- --------
1 Seatbelt worn 390115
2 Seatbelt not worn 10158
3 Child restraint worn 0
4 Child restraint not worn 1
5 Seatbelt/restraint not fitted 1573
6 Helmet worn 60521
7 Helmet not worn 3495
8 Not appropriate 3635
9 Not known 168617
In case it's not clear from the query, VIC_HELMET_BELT is a lookup table that is equivalent to the id and desc columns of the table above. The VIC_PERSONS.HELMET_BELT_WORN contains the values I am counting that correspond to the VIC_HELMET_BELT.id values.
Any commentary on the quality of my original SQL is also appreciated. I feel like I never really had a strong understanding of how to make the most of SQL queries.
In case you're interested, this is crash data from Victoria, Australia. You may have noticed that I have filtered for the driver only. I really hope the "Child restraint not worn" entry is just a typo!
Thanks in advance for your help.
Here you go
SELECT `VIC_HELMET_BELT`.`id`, `VIC_HELMET_BELT`.`HELMET_BELT` AS 'desc',
COUNT(`VIC_VEHICLE`.`ACCIDENT_NO`) AS 'count'
FROM `VIC_HELMET_BELT`
LEFT JOIN `VIC_PERSON` ON `VIC_HELMET_BELT`.`id` = `VIC_PERSON`.`HELMET_BELT_WORN` AND `VIC_PERSON`.`SEATING_POSITION_Id` = 1
LEFT JOIN `VIC_ACCIDENT` ON `VIC_PERSON`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
LEFT JOIN `VIC_NODE` on VIC_NODE`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
LEFT JOIN `VIC_VEHICLE` on `VIC_VEHICLE`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
GROUP BY `VIC_HELMET_BELT`.`id, `VIC_HELMET_BELT`.`HELMET_BELT`
Try this query below and see if there's any count result for ID=3. If it's null, you can use IFNULL(COUNT(*),0) AS 'Count':
SELECT `VIC_HELMET_BELT`.`id`, `VIC_HELMET_BELT`.`HELMET_BELT` AS 'desc',
COUNT(*) AS 'count'
FROM `VIC_HELMET_BELT`
LEFT JOIN `VIC_PERSON` ON `VIC_HELMET_BELT`.`id` = `VIC_PERSON`.`HELMET_BELT_WORN`
LEFT JOIN `VIC_ACCIDENT` ON `VIC_PERSON`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
LEFT JOIN `VIC_NODE` ON `VIC_NODE`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
LEFT JOIN `VIC_VEHICLE` ON `VIC_VEHICLE`.`ACCIDENT_NO` = `VIC_ACCIDENT`.`ACCIDENT_NO`
WHERE `VIC_PERSON`.`SEATING_POSITION_Id` = 1
GROUP BY `VIC_HELMET_BELT`.`id`
As for the comment on your query, you can try using aliases on your table for shorter query .. example below:
SELECT v1.`id`, v1.`HELMET_BELT` AS 'desc', COUNT(*) AS 'count'
FROM `VIC_HELMET_BELT` as v1
LEFT JOIN `VIC_PERSON` as v2 ON v1.`id` = v2.`HELMET_BELT_WORN`
LEFT JOIN `VIC_ACCIDENT` as v3 ON v2.`ACCIDENT_NO` = v3.`ACCIDENT_NO`
LEFT JOIN `VIC_NODE` as v4 ON v2.`ACCIDENT_NO` = v4.`ACCIDENT_NO`
LEFT JOIN `VIC_VEHICLE` as v5 ON v2.`ACCIDENT_NO` = v4`.`ACCIDENT_NO`
WHERE v2.`SEATING_POSITION_Id` = 1
GROUP BY v1.`id`;
Each of your table being assigned with v1, v2, v3 ... and so on so that when you want to use a column from the table, you don't need to type the full table name instead you just need to type the alias.
**Edit:**Another way you can do is like this, assuming that you need to perform other operation on other tables except for VIC_HELMET_BELT; which just solely become a reference table.
SELECT v1.`id`, v1.`HELMET_BELT` AS 'desc', COUNT(*) AS 'count'
FROM `VIC_HELMET_BELT` AS v1
LEFT JOIN
(SELECT * FROM `VIC_PERSON` AS v2
INNER JOIN `VIC_ACCIDENT` AS v3 ON v2.`ACCIDENT_NO` = v3.`ACCIDENT_NO`
INNER JOIN `VIC_NODE` AS v4 ON v2.`ACCIDENT_NO` = v4.`ACCIDENT_NO`
INNER JOIN `VIC_VEHICLE` AS v5 ON v2.`ACCIDENT_NO` = v4.`ACCIDENT_NO`
WHERE v2.`SEATING_POSITION_Id` = 1) A
ON v1.`id`=A.`HELMET_BELT_WORN`
GROUP BY v1.`id`;
I'm looking for a (cleaner?) way to do the following:
Let's say I have a table, main, with ~15 columns that looks something like this, with one row per id:
main:
id start end col4 ... col15
666 2014-01-01 2014-06-30 ... ... ...
1234 2015-03-05 2015-05-02 ... ... ...
9876 2014-09-01 2015-01-01 ... ... ...
...(etc)
Then I have another table, events, which may have 0, 1, or many rows per id:
events:
id date code
666 2014-01-20 "code_a"
1234 2015-05-01 "code_b"
666 2014-01-25 "code_c"
666 2014-02-09 "code_z"
... (etc)
and finally I have a table, codes, which has one row per code, giving a description for the code as well as a type (0,1, or 2):
codes:
code desc type
"code_a" "something" 0
"code_b" "somethn else" 1
"code_c" "another thing" 0
"code_d" "one more" 2
(no code z)
and what I want as a result is main's 15 columns plus three additional columns which contain comma separated lists of event codes which happened between the start and end dates for that id by type (first column is type 0, second type 1, third type 2), so:
id start end ... col15 type_0 type_1 type_2
666 2014-01-01 2014-06-30 ... ... "code_a,code_c"
1234 2015-03-05 2015-05-02 ... ... "code_b"
...(etc)
my solution is
select m.*
, group_concat(c0.code) as type_0
, group_concat(c1.code) as type_1
, group_concat(c2.code) as type_2
from main m
left join events e on m.id = e.id and e.date between m.start and m.end
left join codes c0 on c0.code = e.code and c0.type = 0
left join codes c1 on c1.code = e.code and c1.type = 1
left join codes c2 on c2.code = e.code and c2.type = 2
group by m.id
, m.start
, m.end
, m.col4
, m.col5
, m.col6
, m.col7
, m.col8
, m.col9
, m.col10
, m.col11
, m.col12
, m.col13
, m.col14
, m.col15
But to me that's pretty nasty looking. Is there a more elegant way to do this (especially avoiding the 15 columns listed in the group by)?
In MySQL, you can just use GROUP BY m.id. Unless you enable the ONLY_FULL_GROUP_BY option, it allows you to use non-aggregate columns that aren't in the GROUP BY clause. This could produce unprectable results if you selected columns that were not uniquely identified by the grouping column, but that's not the case here -- you're grouping by a column that's the unique ID for the m table, and all the non-aggregate columns are from that same table.
In strict SQL, you would have to do it by doing the GROUP_CONCATs in a subquery, which you then join with the main table.
SELECT *
FROM (SELECT m.id,
, group_concat(c0.code) as type_0
, group_concat(c1.code) as type_1
, group_concat(c2.code) as type_2
FROM main m
left join events e on m.id = e.id and e.date between m.start and m.end
left join codes c0 on c0.code = e.code and c0.type = 0
left join codes c1 on c1.code = e.code and c1.type = 1
left join codes c2 on c2.code = e.code and c2.type = 2
GROUP BY m.id
) t1
JOIN main m ON t1.id = m.id
With the "one row per id" specification, you can take advantage of the MySQL extension to the GROUP BY which allows you to include non-aggregates in the SELECT list. The only change required to your query would be to just
GROUP BY m.id
Other databases would throw an error with that. We can get MySQL to throw an error too, if we include ONLY_FULL_GROUP_BY in the sql_mode for the session.
Another alternative would be to avoid a GROUP BY operation on m, using an inline view. You still need to do a GROUP BY, but you can do that in the inline view, where the other columns from main aren't returned, we only return the unique id value. We need that for the join in the outer query.
Also seems like you only need one join to the codes table; you could use a conditional test inside the GROUP_CONCAT to conditionally return the value of the code.
For example:
SELECT m.*
, g.type_0
, g.type_1
, g.type_2
FROM main m
LEFT
JOIN ( SELECT a.id
, GROUP_CONCAT(IF(c.type=0,c.code,NULL)) AS type_0
, GROUP_CONCAT(IF(c.type=1,c.code,NULL)) AS type_1
, GROUP_CONCAT(IF(c.type=2,c.code,NULL)) AS type_2
FROM main a
LEFT
JOIN events e
ON e.id = a.id
AND e.date BETWEEN a.start AND a.end
LEFT
JOIN codes c
ON c.code = e.code
AND c.type IN (0,1,2)
GROUP BY a.id
) g
ON g.id = m.id
I'm not sure that either of those qualifies as "a more elegant way" or not. (Both of these depend on the id column being UNIQUE in main. The second query also relies on id being non-NULL.)
You might want to consider adding an ORDER BY inside the GROUP_CONCAT, for a more deterministic result. It's also possible to include the DISTINCT keyword inside the GROUP_CONCAT, if there's no reason to return "duplicate" values of code in the list, e.g.
GROUP_CONCAT(DISTINCT IF(c.type=0,c.code,NULL) ORDER BY 1)\
Also be aware that the maximum length of the value returned from GROUP_CONCAT is limited to group_concat_max_len.
Another shorter version would be like below by getting the grouping first and then join with it.
select m.*
, XX.type_0
, XX.type_1
, XX.type_2
from main m
left join events e on m.id = e.id and e.date between m.start and m.end
left join (
select code, GROUP_CONCAT(case when type = 0 then code else null end SEPARATOR ', ') AS type_0,
GROUP_CONCAT(case when type = 1 then code else null end SEPARATOR ', ') AS type_1,
GROUP_CONCAT(case when type = 2 then code else null end SEPARATOR ', ') AS type_2
from codes
group by <some_column> )XX ON XX.code = e.code;
I'm trying to get multiple attributes (e.g bedrooms, bathrooms, etc) from a few tables yes I can only seem to get one attribute with my current query. What am I missing?
What I am ultimately trying to find is property_id's with 2 bathrooms and 2 bedrooms. In this case, my desired results are property id's 1 and 4.
Here's my example on sqlfiddle
Example tables:
property
--------
id
1
2
3
4
attribute
---------
id name
1 bedrooms
2 bathrooms
attribute_value
---------------------
id attribute_id value
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 3
property_attributes
------------------------------
property_id attribute_value_id
1 2
1 5
2 2
2 4
3 1
3 4
4 2
4 5
This query can produce the results I want if I only want to see a query based on single attribute:
SELECT p.property_id
FROM property p
INNER JOIN property_attribute pa ON p.property_id = pa.property_id
INNER JOIN property_area pc ON p.property_id = pc.property_id
WHERE pa.attribute_value_id
IN (
SELECT av.attribute_value_id
FROM attribute_value av
INNER JOIN attribute a ON av.attribute_id = a.attribute_id
WHERE a.name like 'bedrooms' AND av.value like '2')
But what must I do to get results with more attributes (e.g. WHERE a.name like 'bathrooms' AND av.value like '2')? If I add another subquery in the main where clause, it doesn't return any results.
/// EDIT ///
here is the solution that worked for me if anyone else winds up in such a situation:
SELECT p.property_id
FROM property p
INNER JOIN property_attribute pa ON p.property_id = pa.property_id
INNER JOIN property_attribute pa2 ON p.property_id = pa2.property_id
INNER JOIN property_attribute pa3 ON p.property_id = pa3.property_id
WHERE pa.attribute_value_id = 2
AND pa2.attribute_value_id = 5
AND pa3.attribute_value_id = 7
This makes usage of a self-join.
You have an awful lot of indirection in your schema. Is there any particular point why you need the property_attribute table? Do you expect a single attribute value to be shared by many properties?
In any case, here is the query for you. Very much in the spirit of the two possible duplicates I mentioned above.
SELECT property.property_id,
bedrooms.value AS bedrooms,
bathrooms.value AS bathrooms
FROM property,
attribute AS bedrooms_attr,
attribute_value AS bedrooms,
property_attribute AS bedrooms_prop,
attribute AS bathrooms_attr,
attribute_value AS bathrooms,
property_attribute AS bathrooms_prop
WHERE bedrooms_attr.name = 'bedrooms'
AND bedrooms.attribute_id = bedrooms_attr.attribute_id
AND bedrooms.attribute_value_id = bedrooms_prop.attribute_value_id
AND bedrooms_prop.property_id = property.property_id
AND bedrooms.value = 2
AND bathrooms_attr.name = 'bathrooms'
AND bathrooms.attribute_id = bathrooms_attr.attribute_id
AND bathrooms.attribute_value_id = bathrooms_prop.attribute_value_id
AND bathrooms_prop.property_id = property.property_id
AND bathrooms.value = 2
Thanks for providing a fiddle with data to play with.
I have the 3 Following Tables:
objects:
ObjectID ObjectDescription
1 "first"
2 "second"
attributes:
AttributeID AttributeDescription
1 "att1"
2 "att2"
3 "att3"
4 "att4"
attributelink:
AttributeID ObjectID
1 1
2 1
4 1
Now my question: I want now have some attributes selected and want to know, which Object has all my selected Attributes. I've tried the following:
SELECT * FROM `objects`
INNER JOIN `attributelink`
ON `objects`.`ObjectID` = `attributelink`.`ObjectID`
WHERE `attributelink`.`AttributeID` =1 AND `attributelink`.`AttributeID` =2
GROUP BY `objects`.`ObjectID`
That obviously doesn't work, because one row can't have 2 AttributeIDs, but how can I archieve this?
You have to join on the attributelink table once for each selected attribute you want to check for:
SELECT o.ObjectID
FROM objects o
INNER JOIN attributelink a1 ON o.ObjectID = a1.ObjectID AND a1.AttributeID = 1
INNER JOIN attributelink a2 ON o.ObjectID = a2.ObjectID AND a2.AttributeID = 2
GROUP BY o.ObjectID
Your test data doesn't show a whole lot about whether it works or not, but FWIW, here's the sqlfiddle.
Another way to do it is to use COUNT DISTINCT with HAVING and GROUP BY (sqlfiddle):
SELECT o.ObjectID
FROM objects o
INNER JOIN attributelink a ON o.ObjectID = a.ObjectID
WHERE a.AttributeID IN (1,2) --here you filter the rows on the attributes to test
GROUP BY o.ObjectID
HAVING COUNT(DISTINCT(a.AttributeID)) = 2 --# of attributes, means "having ALL"
I have this data in a table, for instance,
id name parent parent_id
1 add self 100
2 manage null 100
3 add 10 200
4 manage null 200
5 add 20 300
6 manage null 300
How can I left join or inner join this table itself so I get this result below?
id name parent
2 manage self
4 manage 10
6 manage 20
As you can I that I just want to query the row with the keyword of 'manage' but I want the column parent's data in add's row as the as in manage's row in the result.
Is it possible?
EDIT:
the simplified version of my actual table - system,
system_id parent_id type function_name name main_parent make_accessible sort
31 30 left main Main NULL 0 1
32 31 left page_main_add Add self 0 1
33 31 left page_main_manage Manage NULL 0 2
my actual query and it is quite messy already...
SELECT
a.system_id,
a.main_parent,
b.name,
b.make_accessible,
b.sort
FROM system AS a
INNER JOIN -- self --
(
SELECT system_id, name, make_accessible, sort
FROM system AS s2
LEFT JOIN -- search --
(
SELECT system_id AS parent_id
FROM system AS s1
WHERE s1.function_name = 'page'
) AS s1
ON s1.parent_id = s2.parent_id
WHERE s2.parent_id = s1.parent_id
AND s2.system_id != s1.parent_id
ORDER BY s2.sort ASC
) b
ON b.system_id = a.parent_id
WHERE a.function_name LIKE '%manage%'
ORDER BY b.sort ASC
result I get currently,
system_id main_parent name make_accessible sort
33 NULL Main 0 1
but I am after this,
system_id main_parent name make_accessible sort
33 self Main 0 1
You just need to reference the table twice:
select t1.id, t1.name, t2.id, t2.name
from TableA t1
inner join TableA t2
on t1.parent_id = t2.Id
Replace inner with left join if you want to see roots in the list.
UPDATE:
I misread your question. It seems to me that you always have two rows, manage one and add one. To get to "Add" from manage:
select system.*, (select parent
from system s2
where s2.parent_id = system.parent_id
and s2.name = 'add')
AS parent
from system
where name = 'manage'
Or, you might split the table into two derived tables and join them by parent_id:
select *
from system
inner join
(
select * from system where name = 'add'
) s2
on system.parent_id = s2.parent_id
where system.name = 'manage'
This will allow you to use all the columns from s2.
Your data does not abide to a child-parent hierarchical structure. For example, your column parent holds the value 10, which is not the value of any id, so a child-parent association is not possible.
In other words, there's nothing that relates the record 2,manage,null to the record 1,add,self, or the record 4,manage,null to 3,add,10, as you intend to do in your query.
To represent hierarchical data, you usually need a table that has a foreign key referencing it's own primary key. So your column parent must reference the column id, then you can express a child-parent relationship between manage and add. Currently, that's not possible.
UPDATED: Joining by parent_id, try:
select m.id, m.name, a.parent
from myTable m
join myTable a on m.parent_id = a.parent_id and a.name = 'add'
where m.name = 'manage'
Change the inner join to a left join if there may not be a corresponding add row.