Counting referring rows in a MySQL table - mysql

I have a table that looks like this:
Id | Name | Parent
---+-------------------------+-------
1 | Parent One | 0
2 | Child of Parent One | 1
3 | Parent Two | 0
4 | Parent Three | 0
5 | Parent Four | 0
6 | Child of 1st Parent | 1
7 | Child of 2nd Parent | 3
8 | Child of 3nd Parent | 4
The table does not represent a hierarchy: Every item is either a child or a parent, but not both.
I'd like to run a query on it that returns this:
Id | Name | ChildCount
---+-------------------------+-----------
1 | Parent One | 2
3 | Parent Two | 1
4 | Parent Three | 1
5 | Parent Four | 0
I guessed that this might work, but it didn't:
SELECT parents.id, parents.name, COUNT(parents.id = children.parent) AS childCount
FROM (SELECT * FROM items WHERE parent = 0) parents,
(SELECT * FROM items WHERE parent > 0) children
How should I be doing this?

SELECT a.id, a.Name, COUNT(b.id) as ChildCount
FROM table1 a
LEFT JOIN table1 b ON (b.Parent = a.id)
GROUP BY a.id [,a.Name] // ,a.Name is not mandatory for mysql, `GROUP BY a.id` is enough
You may also want to add WHERE a.Parent = 0 to show only parent rows.
Updated (COUNT(*) changed to COUNT(b.id) )
For Eldest Child:
SELECT x.id, x.Name, x.ChildCount, c.Name AS eldest_child_name
FROM
(
SELECT a.id, a.Name, COUNT(b.id) as ChildCount, MAX(b.id) as max_child_id
FROM table1 a
LEFT JOIN table1 b ON (b.Parent = a.id)
GROUP BY a.id
)X
LEFT JOIN table1 c ON (c.id = X.max_child_id)

Related

Display child and parent relationship (if any) in a same table

I have this table
| id |parent|name|
| 1 | NULL | E |
| 2 | NULL | B |
| 3 | 5 | U |
| 4 | 5 | X |
| 5 | NULL | C |
| 6 | NULL | A |
I would like the list, ordered by parent's name, of all ID whether they have a parent or not:
| id |parent|name|has_child|
| 6 | NULL | A | 0 |
| 2 | NULL | B | 0 |
| 5 | NULL | C | 1 |
| 3 | 5 | U | 0 |
| 4 | 5 | X | 0 |
| 1 | NULL | E | 0 |
Is it possible?
I have tried many things but never get the proper answer, and I don't really know how to add the 'has_child' column
SELECT
t1.parent,
t2.name
FROM tablename AS t1
INNER JOIN
(
SELECT MIN(id) AS id, parent
FROM tablename
GROUP BY parent
) AS t22 ON t22.id = t1.id AND t1.parent = t22.parent
INNER JOIN tablename AS t2 ON t1.parent = t2.id;
I would use a self join here:
SELECT DISTINCT
t1.id,
t1.parent,
t1.name,
1 - ISNULL(t2.id) has_child
FROM tablename t1
LEFT JOIN tablename t2
ON t1.id = t2.parent
ORDER BY
t1.id;
The join condition used here, which matches a given record as a parent to one or more children, is that the current id is also the parent of some other record(s). Note that we need SELECT DISTINCT here, because a given parent might match to more than one child record.
You can use a self join -- because you want the name of the parent and not the id -- and coalesce() for ordering:
select t.*,
(case when exists (select 1 from t tc where tc.parent = t.id)
then 1 else 0
end)
from t left join
t tp
on t.parent = tp.id
order by coalesce(tp.name, t.name), -- group rows by the parent, if any
(tp.name is null) desc, -- put parent first
t.name; -- order by children
I hope that you find this answer a little bit useful. The subquery gets the distinct id of parents and excludes the blanked fills.
SELECT *,
CASE WHEN id IN (SELECT DISTINCT parent
FROM tablename
WHERE parent IS NOT NULL)
THEN '1' ELSE '0'
END AS has_child
FROM tablename
ORDER BY name;
SELECT t1.id, t1.parent, t1.name, MAX(t2.parent is not null) has_child
FROM table t1
LEFT JOIN table t2 ON t1.id = t2.parent_id
GROUP BY t1.id, t1.parent, t1.name

sql - Get top parent of specified record

I have a standard nested category tree:
| id | parent_id | name |
+----+-----------+----------------+
| 1 | 0 | Category 1 |
| 2 | 0 | Category 2 |
| 3 | 0 | Category 3 |
| 4 | 1 | Category 1.1 |
| 5 | 1 | Category 1.2 |
| 6 | 2 | Category 2.1 |
| 7 | 2 | Category 2.2 |
| 8 | 7 | Category 2.2.1 |
and now I need to get top parent of specified item so I do:
SELECT
cat.*
FROM
categories cat
LEFT JOIN
categories subCat
ON
subCat.parent_id = cat.id
AND cat.parent_id = 0
WHERE
subCat.id = 5;
and if item is first-level child, it's working ok but is item is second-level child (eg. 8) I'm not getting records - how to do this?
Here is SQlFiddle: http://sqlfiddle.com/#!9/5879bd/11
UPDATE
Here is real example: http://sqlfiddle.com/#!9/6f1d1c/1
I want to get parent category of Xiaomi
With MySQL 5.6 you cannot use recursive CTEs.
To do it properly, for an arbitrary tree depth, you need to write a function/procedure, that traverses the hierarchy and returns the top node once reached.
As a workaround, when the maximum number of level d is set, you can left join the parent (d - 1) times. Use coalesce() to get the first non null value along the path. So in your case, for d = 3:
SELECT c.*
FROM categories c
INNER JOIN (SELECT coalesce(c3.id, c2.id, c1.id) id
FROM categories c1
LEFT JOIN categories c2
ON c2.id = c1.parent_id
LEFT JOIN categories c3
ON c3.id = c2.parent_id
WHERE c1.id = 10) t
ON t.id = c.id;
(I first select the ID of the top node and inner join the rest, to avoid coalesce() on all columns. It might give a false result on nullable columns if the value for the column in the top node is null but not for any child node. It should display NULL then, but will falsely show the non value from the child node.)
But note: It will fail if the depth grows!
This answers the original version of the question.
To get the top level, you can use the name column:
SELECT c.*
FROM categories c JOIN
categories sc
ON sc.id = 10 AND
c.name = SUBSTRING_INDEX(sc.name, '.', 1);

MySQL: select parent when child matches condition

i have the following situation.
Table "parent" has parent objects:
+-----+-------------------+
| UID | some_parent_stuff |
+-----+-------------------+
| 1 | value01 |
+-----+-------------------+
| 2 | value02 |
| | |
The table child has objects with a reference column to parents and a status:
+-----+-------------+------------+
| UID | parent_uid | status_uid |
+-----+-------------+------------+
| 1 | 2 | 1 |
+-----+-------------+------------+
| 2 | 2 | 5 |
+-----+-------------+------------+
| 3 | 1 | 2 |
| | | |
Now i'd like to select all parents, from which the child with the highest UID has a status_uid in a list (i.e. IN(1,5)).
I have something like this:
SELECT P.uid FROM parent P
INNER JOIN child C ON C.parent_uid = P.uid AND C.status_uid IN(1)
GROUP BY P.uid;
This returns parent with UID 2 too. But it has a child with a higher UID, which has a status not in the IN-Clause. So i dont want to select it.
I hope you understand my question, my english isn't the best... Thx for ideas and with best regards
Kjuuze
What I think you need is a query within your query (PreQuery in my sample). The inner query gets on a per parent id basis, the maximum child-level uid regardless of the qualifying status ID. Since this is grouped by parent, it will return two records per your example data.
Child Table
UID Parent Status
2 2 5
3 1 2
Now, you can join to both the parent table and the child table from this prequery on the respective IDs, and at the second CHILD-LEVEL join, add your criteria for the status in (1,5)
SELECT
p.uid,
p.some_parent_stuff,
c2.uid,
c2.status_uid
from
( select c.parent_uid,
MAX( c.uid ) as ChildUID
from
child c
group by
c.parent_uid ) PreQuery
JOIN Parent p
ON PreQuery.parent_uid = p.uid
JOIN Child c2
ON PreQuery.ChildUID = c2.uid
AND c2.status_id in ( 1, 5 )
This will return only the 1 record for Child ID 2, Parent 2, Status 5. Since the Child ID of 3 has a status of 2, it will NOT be returned in final result set.
SELECT P.uid
FROM parent P
INNER JOIN child C ON C.parent_uid = P.uid AND C.status_uid IN(1)
WHERE C.status_uid = (
SELECT MAX(status_uid)
FROM child)
GROUP BY P.uid;
If I understood what you are asking this should work, however this is not tested, you can also review mysql sub queriesmysql here

JOIN MySQL Tables

I have following tables format:
members
mem_id | mem_name
------------------
1 A
2 B
3 C
project_members
fk_mem_id | meb_parent_id
-------------------------
1 0
2 1
3 2
and I require result like:
mem_id | child| parent
--------------------------
1 A NULL
2 B A
3 C B
I tried but fail.. any idea....?
SELECT pm.*, m1.meb_name as child, m2.meb_name as parent
FROM members m1
LEFT JOIN members m2
ON m1.meb_parent_id = m2.meb_id
SELECT project_members.fk_mem_id,
a.mem_name AS child,
b.mem_name AS parent
FROM project_members
INNER JOIN members a
ON project_members.fk_mem_id = a.mem_id
LEFT JOIN members b
ON project_members.meb_parent_id = b.mem_id

SQL nested order by?

I'm sure that this has been asked before, but I don't know what to call it exactly to find the answer.
I have a table of categories and sub categories. They each have an id and a parent id. If it is a top level category, the parent id is 0. Sub categories have the parent id set to the
category id of it's parent.
category_id # The ID for this record
category_name # The name of the category
parent_id # The parent ID for this category
display_order # Order of categories within their grouping
1 A 0 0 # First primary category
2 a1 1 0 # Subcategory, parent is A, display_order is 0
3 a2 1 1
4 a3 1 2
5 B 0 1 # Second primary category
6 b1 5 0 # Subcategory, parent is B, display_order is 0
7 b2 5 1
8 b3 5 2
I'm trying to write an SQL query that will give me all of the categories in this order:
A, a1, a2, a3, B, b1, b2, b3
SELECT * FROM categories ORDER BY display_order
Is this possible in SQL, or will I need to use multiple queries
Thanks,
Brad
Something like this might maybe work:
SELECT *
FROM categories
ORDER BY IF(parent_id, parent_id, category_id), parent_id, display_order
but since it can't use an index, it'll be slow. (Didn't test though, might be wrong)
The first ORDER BY condition sorts parents and children together; then the second one ensures the parent precedes its children; the third sorts the children among themselves.
Also, it will obviously work only in the case you directly described, where you have a two-level hierarchy.
an answer has already been accepted, but i thought i would share my thoughts on this anyways. i tried to sort the main categories after their display_order column as well. here's my table
mysql> select * from categories;
+-------------+---------------+-----------+---------------+
| category_id | category_name | parent_id | display_order |
+-------------+---------------+-----------+---------------+
| 1 | B | 0 | 2 |
| 2 | C | 0 | 3 |
| 3 | b2 | 1 | 2 |
| 4 | b1 | 1 | 1 |
| 5 | c3 | 2 | 3 |
| 6 | A | 0 | 1 |
| 7 | c2 | 2 | 2 |
| 8 | b3 | 1 | 3 |
| 9 | a2 | 6 | 2 |
| 10 | a1 | 6 | 1 |
| 11 | c1 | 2 | 1 |
| 12 | a3 | 6 | 3 |
+-------------+---------------+-----------+---------------+
12 rows in set (0.00 sec)
as you see, i have taken great care to add the categories in a none linear order :)
my query:
SELECT
sub_id AS category_id,
sub_name AS category_name,
sub_parent_id AS parent_id,
main_order + sub_order AS display_order
FROM (
SELECT
c1.display_order + c1.display_order * (
SELECT
inner_c.display_order
FROM
categories AS inner_c
WHERE
inner_c.parent_id <> 0
ORDER BY
inner_c.display_order DESC
LIMIT 1) AS main_order,
c2.display_order AS sub_order,
c2.category_name AS sub_name,
c2.category_id AS sub_id,
c2.parent_id AS sub_parent_id
FROM
categories AS c1
JOIN
categories AS c2
ON
c1.category_id = c2.parent_id
WHERE
c1.parent_id = 0
) AS renumbered
UNION ALL
SELECT
category_id,
category_name,
parent_id,
display_order + display_order * (
SELECT
inner_c.display_order
FROM
categories AS inner_c
WHERE
inner_c.parent_id <> 0
ORDER BY
inner_c.display_order DESC
LIMIT 1) AS display_order
FROM
categories
WHERE
parent_id = 0
ORDER BY
display_order;
Sounds almost identical to another I've answered with similar parent/child hierarchy while retaining child elements at same grouped level as its corresponding parent...Check this thread
Whenever possible, I build SQL incrementally, not least because it gives me the option of testing as I go.
The first thing we need to be able to do is identify the top-level categories:
SELECT category_id AS tl_cat_id,
category_name AS tl_cat_name,
display_order AS tl_disp_order
FROM Categories
WHERE parent_id = 0;
Now we need to join that with the categories and subcategories to get the result:
SELECT t.tl_cat_id, t.cat_name, t.tl_disp_order, c.category_id, c.category_name,
CASE WHEN c.parent_id = 0 THEN 0 ELSE c.display_order END AS disp_order
FROM Categories AS c
JOIN (SELECT category_id AS tl_cat_id,
category_name AS tl_cat_name,
display_order AS tl_disp_order
FROM Categories
WHERE parent_id = 0) AS t
ON c.tl_cat_id = t.parent_id OR (c.parent_id = 0 AND t.tl_cat_id = c.category_id)
ORDER BY tl_disp_order, disp_order;
The join condition is unusual but should work; it collects rows where the parent ID is the same as the current category ID, or rows where the parent ID is 0 but the category ID is the same. The ordering is then almost trivial - except that when you are dealing with the sub-category ordering, you want the parent item at the front of the list. The CASE expression handles that mapping.