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
Related
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);
I have the following table:
+----+--------+
| id | parent |
+----+--------+
| 1 | 4 |
| 2 | 1 |
| 3 | NULL |
| 4 | NULL |
| 5 | 2 |
| 6 | 3 |
+----+--------+
I want this table to be ordered like this:
+----+--------+------------------------------------------------------------+
| id | parent | Why it has to be ordered like this |
+----+--------+------------------------------------------------------------+
| 5 | 2 | 5 has parent 2 has parent 1 has parent 4. So 3 rows above. |
| 2 | 1 | 2 has parent 1 has parent 4. So 2 rows above. |
| 1 | 4 | 1 has parent 4. So 1 row above. |
| 6 | 3 | 6 has parent 3. So 1 row above. |
| 4 | NULL | No parent. So 0 rows above. |
| 3 | NULL | No parent. So 0 rows above. |
+----+--------+------------------------------------------------------------+
So I want to recursively count the ancestors of a row and sort on that. How can I do that?
Edit: I'm on MySQL version 5.7.21.
You could do this with a recursive CTE, but you didn't list your mysql version and not all versions can do that, so here is something that should work even for older versions. This does the recursion itself with a temporary table and a while statement. The temporary table gets built with one record for each record in the main table, which holds the parent count data. First we do all records with no parent, then the query inside the while does all the records for the next generation. Note that the syntax may be a little bit off, I haven't done mysql for some time.
--Create temp table to hold the parent count data
CREATE TEMPORARY TABLE ParentCount (id int, pcount int);
--First create a pcount record with count zero for all records with no parent
insert into ParentCount (id, pcount) Select id, 0 from TestData where parent is null;
--If we don't have a parentcount set for every record, keep going
-- This will run once for every level of depth
While (Select COUNT(id) from TestData) <> (Select COUNT(id) from ParentCount) Begin
--add a pcount record for all rows that don't have one yet, but whose
-- parents do have one (ie the next generation)
insert into ParentCount (id, pcount)
Select T.id, P.pcount + 1 as newpcount
from TestData T
inner join ParentCount P on P.id = T.parent
left outer join ParentCount P2 on P2.id = T.id
where P2.id is null;
End;
--final query
Select T.id, T.parent
from TestData T
inner join Parents P on T.id = p.id
order by P.pcount DESC, T.id ASC;
There are 2 tables with 1: M relation.
The join output shall have 2 columns:
1st column - from Parent table and
2nd column I a concatenation of all related records from the child table.
A total number of records in output shall be # of records in the parent table.
Database: MySQL.
Example:
Parent(id PK, parent_name)
Child (id PK, parent_id FK, child_name)
Parent:
| id | parent_name
——————------------
| 1 | Smith
| 2 | David
Child :
id | parent_id | child_name
———————————————---------------
1 | 1 | Anna
2 | 1 | Linda
3 | 2 | Maria
4 | 2 | Michael
Expected join result:
parent_name | child_name
——————------|———————
Smith | Anna, Linda
David | Maria, Michael
You need group_concat. Try this -
SELECT P.parent_name, GROUP_CONCAT(C.child_name)
FROM Parent P INNER JOIN Child C
ON P.id = C.parent_id
GROUP BY P.parent_name
Hope this helps.
Select pr.parent_name,group_concat(ch.child_name)
from parent pr join child ch
where pr.id = ch.parent_id
group by pr.parent_name;
Group concat is function in MySQL. You can use that.
Give a try :)
I have two tables
Parent:
+--+---------+
|id| text |
+--+---------+
| 1| Blah |
| 2| Blah2 |
| 3| Blah3 |
+--+---------+
Children
+--+---------+---------+
|id|parent_id| table_id|
+--+---------+---------+
| 1| 1 | 1 |
| 2| 1 | 3 |
| 3| 2 | 2 |
+--+---------+---------+
I want to find parent having children with table_id both 1 and 3.
Currently I am using following query
SELECT *
FROM Parent
WHERE id
IN (
SELECT parent_id
FROM Children
WHERE table_id = 1
)
AND id
IN (
SELECT parent_id
FROM Children
WHERE table_id = 3
)
As I have thousands of records, this query runs very slow.
Is there altenative faster query to execute it?
You can let mysql EXPLAIN its execution plan to you: check this chapter in the manual. From there, you can optimize your query for speed. There are different approaches to that, it's a broad field - but in general, it's a good start to minimize the number and the width of the included joins and inner queries.
There are many ways of doing it, and one way with proper indexing would make it pretty fast as
select p.* from parent p
join children c on c.parent_id = p.id
where c.table_id = 1
and exists (
select 1 from children c1
where c1.parent_id = c.parent_id
and c1.table_id = 3
);
The indexes that could be added as
alter table parent add index id_idx(id);
alter table children add index parent_idx(parent_id);
alter table children add index table_id_idx(table_id);
SELECT c.parent_id, p.text,
SUM(CASE WHEN c.table_id=1 THEN 1 ELSE 0 END) as t_1,
SUM(CASE WHEN c.table_id=3 THEN 1 ELSE 0 END) as t_3
FROM Children c
LEFT JOIN Parent p
ON c.parent_id = p.id
WHERE c.table_id IN ( 1, 3)
GROUP BY c.parent_id
HAVING t_1>0 AND t_3>0
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)