List products by parent id's - mysql

table: products
____________________________
|id | cat_id | title |
|----------------------------|
| 1 | 14 | new bmw X5 |
| 2 | 9 | barbie horse |
| 3 | 8 | cool iphone 11|
| 4 | 10 | new galaxy S6 |
|----------------------------|
table: categories
_______________________________
|id | cat_name | parent_id |
|-------------------------------|
| 1 | smartphones | NULL |
| 2 | cars | NULL |
| 3 | toys | NULL |
| 4 | Apple | 1 |
| 5 | Nokia | 1 |
| 6 | Samsung | 1 |
| 7 | iphone 10 | 4 |
| 8 | iphone 11 | 4 |
| 9 | galaxy s5 | 6 |
| 10 | galaxy s6 | 6 |
| 11 | Audi | 2 |
| 12 | BMW | 2 |
| 13 | X3 | 12 |
| 14 | X5 | 12 |
| 15 | A6 | 11 |
| 16 | barbie | 3 |
| 17 | transformer | 3 |
| 18 | horse | 16 |
|-------------------------------|
i found a way to list the maincategories of every product
select distinct id as subcat_id, cat_name as subcat_name, parent_id
from (
select d6.parent_id as id6,
d5.parent_id as id5,
d4.parent_id as id4,
d3.parent_id as id3,
d2.parent_id as id2,
d1.parent_id as id1,
d1.id as id0,
from categories d1
left join categories d2 on d2.id = d1.parent_id
left join categories d3 on d3.id = d2.parent_id
left join categories d4 on d4.id = d3.parent_id
left join categories d5 on d5.id = d4.parent_id
left join categories d6 on d6.id = d5.parent_id
left join products pro d1.id = pro.cat_id
) as h
inner join categories d on d.id in (id0, id1, id2, id3, id4, id5, id6)
where d.parent_id IS NULL order by 1
but i want to have:
show all products from category smartphones (id 1)
or
show me all products from category smartphones (id 1) AND Apple (id 4)
but how? recursiv search to all categories

You can use a Recursive CTE (Common Table Expression) in MySQL 8.x. For example:
with recursive
n as (
select
p.id, p.cat_id, p.title,
c.id as cid, c.parent_id as pid
from products p
join categories c on c.id = p.cat_id
union all
select
n.id, n.cat_id, n.title,
c.id as cid, c.parent_id as pid
from n
join categories c on c.id = n.pid
)
select
n.id, n.title, c.*
from n
join categories c on c.id = n.cid
where n.pid is null

Related

MySQL With Recursive

I need to return my categories with the item quantity in each department, the parent categories must inherit from the child categories the quantity of products in each category.
Categories
+-------------+----------------------+--------+
| id | name | parent_id |
+-------------+----------------------+--------+
| 1 | ELECTRONICS | NULL |
| 2 | TELEVISIONS | 1 |
| 3 | TUBE | 2 |
| 4 | LCD | 2 |
| 5 | PLASMA | 2 |
| 6 | PORTABLE ELECTRONICS | 1 |
| 7 | MP3 PLAYERS | 6 |
| 8 | FLASH | 7 |
| 9 | CD PLAYERS | 6 |
| 10 | 2 WAY RADIOS | 6 |
+-------------+----------------------+--------+
Product
+-------------+----------------------+--------+
| id | product | category_id |
+-------------+----------------------+--------+
| 1 | TV LCD 32" | 4 |
| 2 | TV LCD 45" | 4 |
| 3 | TV TUBE 29" | 3 |
| 3 | IPOD | 7 |
+-------------+----------------------+--------+
Expected result
+-------------+----------------------+------------+
| id | name | level| quantity |
+-------------+----------------------+------------+
| 1 | ELECTRONICS | 1 | 4 |
| 2 | TELEVISIONS | 2 | 3 |
| 3 | TUBE | 3 | 1 |
| 4 | LCD | 3 | 2 |
| 5 | PORTABLE ELECTRONICS | 2 | 1 |
| 6 | MP3 PLAYERS | 3 | 1 |
+-------------+----------------------+------------+
I need to do using with recursive, because the speed is much higher than using nested
WITH RECURSIVE category_path (id, name, level, parent_id) AS
(
SELECT id, name, 1 level, parent_id
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, level + 1, c.parent_id
FROM category_path AS cp
JOIN categories AS c
ON cp.id = c.parent_id
)
SELECT * FROM category_path
Time: 0.020s
using nested
SELECT
parent.id,
parent.name,
parent.parent_id,
COUNT(departaments.product_id)
FROM
categories AS node
INNER JOIN
categories AS parent
ON node.lft BETWEEN parent.lft AND parent.rgt
INNER JOIN
departaments
ON node.id = departaments.categorie_id
GROUP BY
parent.id
ORDER BY
node.lft;
Time: 1.510s
First write a query to get product count per category. This is quite simple:
with products_per_category as (
select c.id, count(p.id) as pcount
from categories c
left join products p on p.category_id = c.id
group by c.id
)
select *
from products_per_category
order by id
db-fiddle
Then write a recursive CTE to generate a transitive closure:
with recursive rcte as (
select c.id, c.id as ancestor_id
from categories c
union all
select r.id, c.parent_id
from rcte r
join categories c on c.id = r.ancestor_id
)
select *
from rcte
order by id, ancestor_id
The result will be like:
| id | ancestor_id |
| --- | ----------- |
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
...
| 9 | 1 |
| 9 | 6 |
| 9 | 9 |
| 10 | 1 |
| 10 | 6 |
| 10 | 10 |
db-fiddle
It's like you get paths from root node to each category. Eg. for 9 the path is 1->6->9
If you order it by ancestor_id first, you will get:
| id | ancestor_id |
| --- | ----------- |
| 1 | 1 |
...
| 10 | 1 |
| 2 | 2 |
| 3 | 2 |
| 4 | 2 |
| 5 | 2 |
| 3 | 3 |
...
db-fiddle
Here you can see, that category 2 (ancestor_id=2) has subcategories (id) 2,3,4,5. Note that every category has itself as subcategory. This will make the next step simpler.
Now all we need is to join the two CTEs and sum up the product counts:
with recursive products_per_category as (
select c.id, count(p.id) as pcount
from categories c
left join products p on p.category_id = c.id
group by c.id
), rcte as (
select c.id, c.id as ancestor_id
from categories c
union all
select r.id, c.parent_id
from rcte r
join categories c on c.id = r.ancestor_id
where c.parent_id is not null
)
select
c.id,
c.name,
sum(p.pcount) as quantity
from rcte r
join categories c on c.id = r.ancestor_id
left join products_per_category p on p.id = r.id
group by c.id
Result:
| id | name | quantity |
| --- | -------------------- | -------- |
| 1 | ELECTRONICS | 4 |
| 2 | TELEVISIONS | 3 |
| 3 | TUBE | 1 |
| 4 | LCD | 2 |
| 5 | PLASMA | 0 |
| 6 | PORTABLE ELECTRONICS | 1 |
| 7 | MP3 PLAYERS | 1 |
| 8 | FLASH | 0 |
| 9 | CD PLAYERS | 0 |
| 10 | 2 WAY RADIOS | 0 |
db-fiddle
If you want to remove empty categories (quantity = 0), then just replace all LEFT JOINs with INNER JOINs.
Update
To get the level, you can use a subquery in the outer SELECT:
(select count(*) from rcte r2 where r2.id = c.id) as level
db-fiddle

mysql - joins with sub queries with sub query where conditions using main query fields

i have a complaint table
|------------------------|
| cid | desc |
|------------------------|
| 1 | faulty |
| 2 | broken |
| 3 | spoiled |
|------------------------|
and an assignment table
|------------------------------------|
| aid | cid | empid |
|------------------------------------|
| 1 | 1 | 1 |
| 2 | 1 | 5 |
| 3 | 2 | 2 |
| 4 | 2 | |
| 5 | 3 | 2 |
| 6 | 3 | 7 |
|------------------------------------|
each complaint can be assigned to atmost two employees
i need to display a list in the below format
|---------------------------------------------------|
| cid | desc | emp1id | emp2id |
|------------------------|--------------------------|
| 1 | faulty | 1 | 5 |
| 2 | broken | 2 | |
| 3 | spoiled | 2 | 7 |
|------------------------|--------------------------|
i wrote the query like this
select c.cid, c.desc, a1.empid as emp1id, a2.empid as emp2id
from complaint c
left join (
select aid, cid, empid
from assignment aa
where aa.cid = c.cid
limit 0,1
) as a1 on a1.cid = c.cid
left join (
select aid, cid, empid
from assignment ab
where ab.cid = c.cid
limit 1,1
) as a2 on a2.cid = c.cid
but it is not working, i am getting error for c.cid in sub queries. how to do?
This might work? (I don't have mySql installed)
select c.cid,
c.desc,
(
select aid, cid, empid
from assignment aa
where aa.cid = c.cid
limit 0,1
) as emp1id,
(
select aid, cid, empid
from assignment ab
where ab.cid = c.cid
limit 1,1
) as emp2id
from complaint c

Make up to get 2 object with native nativeQuery with MAX(ID)

I have a table named Story:
sID | sName | sView
1 | s1 | 1
2 | s2 | 11
3 | s3 | 142
4 | s4 | 152
Table Chapter:
chID | sID | chName | chContent
1 | 1 | ch1 | aaa
2 | 2 | ch2 | aaa
3 | 3 | ch3 | aaa
4 | 1 | ch4 | aaa
5 | 3 | ch5 | aaa
6 | 1 | ch6 | aaa
7 | 2 | ch7 | aaa
NaviteQuery:
SELECT s.*, MAX(c.chID) as chapterID FROM Story s
LEFT JOIN Chapter c ON s.sID = c.sID
GROUP BY s.sID
Result:
sID | sName | sView | chapterID
1 | s1 | 1 | 6
2 | s2 | 11 | 7
3 | s3 | 142 | 5
4 | s4 | 152 | null
But you should get a 'SELECT s. *, c. * FROM ....', who can help me with !!!!
You can write it on some differents way. For example you can use it:
SELECT s.*, c.* FROM Story s
LEFT JOIN (SELECT * FROM Chapter c INNER JOIN (SELECT MAX(c.chID) as chapterID FROM Story s
LEFT JOIN Chapter c ON s.sID = c.sID
GROUP BY s.sID) d ON c.chID = d.chapterID) c ON s.sID = c.sID

SQL Query: 3 tables highest Value plus rest of row plus

i'm currently stuck on a sql-query, trying to find a solution, but making me headache for 2 stays now. i've got 3 tables
user-table:
+-----+----------+-----------+
| pid | username | role |
+-----+----------+-----------+
| 1 | user1 | patient |
| 2 | user2 | patient |
| 3 | user3 | doc |
| 4 | user4 | assistant |
| 5 | user5 | patient |
+-----+----------+-----------+
base-dat:
+-----+---------+-------+------------+
| pid | surname | name | birthdate |
+-----+---------+-------+------------+
| 1 | smith | john | 1950-07-31 |
| 2 | jackson | sarah | 1948-08-15 |
+-----+---------+-------+------------+
med-dat:
+-----+-----+---------------+--------+--------+
| mid | pid | dateLastEntry | weight | pulse |
+-----+-----+---------------+--------+--------+
| 1 | 1 | 2017-12-01 | 86 | 65 |
| 2 | 1 | 2017-12-02 | 84 | 70 |
| 3 | 1 | 2017-12-03 | 80 | 67 |
| 4 | 2 | 2017-11-15 | 66 | 60 |
| 5 | 2 | 2017-11-17 | 60 | 64 |
+-----+-----+---------------+--------+--------+
I'm trying to get the max(dateLastEntry) for each user with role patient, showing their pid, name, surname, weight, pulse in a single row - , even if there is no med-data entry for the patient: something like this:
+-----+---------+-------+------------+--------+-------+
| pid | surname | name | lastEntry | weight | pulse |
+-----+---------+-------+------------+--------+-------+
| 1 | smith | john | 2017-12-02 | 84 | 70 |
| 2 | jackson | sarah | 2017-11-17 | 60 | 64 |
| 5 | NONE | NONE | NONE | NONE | NONE |
+-----+---------+-------+------------+--------+-------+
Atm my statement looks like this, but can't get the proper result:
select b.pid, s.surname, s.name, max(m.date) as lastEntry, m.weight, m.pulse
from users b
left join med-dat m on b.pid = m.pid
left join base-dat s on m.pid = s.pid
where b.role = 'Patient'
group by b.pid, s.surname, s.name, m.weight;
You can rewrite your query as below to get the desired output
select b.pid, s.surname, s.name, m.dateLastEntry as lastEntry, m.weight, m.pulse
from users b
left join med_dat m on b.pid = m.pid
left join base_dat s on m.pid = s.pid
left join med_dat m1 on m.pid = m1.pid
and m.dateLastEntry < m1.dateLastEntry
where m1.pid is null
and b.role = 'Patient'
DEMO
Edit from comment, join base_dat using pid from user table
select b.pid, s.surname, s.name, m.dateLastEntry as lastEntry, m.weight, m.pulse
from users b
left join med_dat m on b.pid = m.pid
left join base_dat s on b.pid = s.pid
left join med_dat m1 on m.pid = m1.pid and m.dateLastEntry < m1.dateLastEntry
where m1.pid is null
and b.role = 'Patient'
group by b.pid, s.surname, s.name, m.weight;
Demo

SELECT rows with MAX value

Well, I have three different tables (check below). I wanted to make that I can see, which is the latest image (req_image_1, etc) saved for each category, which is not parent (cat_parent = 0)
One table, which holds general information about requests
+----+--------------------------+------------+
| id | req_name | req_parent |
+----+--------------------------+------------+
| 3 | Send pack | 19 |
| 4 | Go Visit | 18 |
| 5 | Stop by | 19 |
| 6 | Deliver cookies | 34 |
+----+--------------------------+------------+
Second table, which holds meta information about requests
+----------+------------+------------+----------------------------+
| umeta_id | request_id | meta_key | meta_value |
+----------+------------+------------+----------------------------+
| 1 | 3 | req_city | London |
| 2 | 3 | req_street | 11 Baker street |
| 3 | 3 | req_img_1 | a1c8f69edb37bf6c6.jpg |
| 4 | 4 | req_city | Manchester |
| 5 | 4 | req_street | 71 Main street |
| 6 | 4 | req_img_2 | a71f4160d7f7f7555.jpg |
| 7 | 5 | req_city | Sheffield |
| 8 | 5 | req_street | 240 Duke street |
| 9 | 6 | req_city | Manchester |
| 10 | 6 | req_street | 13 Chapel street |
| 11 | 6 | req_img_1 | 854b9faaa53d8fe02.jpg |
+----------+------------+------------+----------------------------+
Third table, which holds information about categories
+----+------------------------+------------+
| ID | cat_name | cat_parent |
+----+------------------------+------------+
| 1 | Category_01 | 0 |
| 6 | Category_02 | 0 |
| 18 | Category_01_01 | 1 |
| 19 | Category_01_02 | 1 |
| 34 | Category_02_01 | 6 |
+----+------------------------+------------+
So far I managed, that I could get all images for each category with this query:
SELECT cat.cat_parent AS category, req.ID, meta.meta_value AS image
FROM d_requests req
LEFT JOIN d_requests_meta meta ON ( req.ID = meta.request_id )
LEFT JOIN d_categories cat ON ( req.req_parent = cat.ID )
WHERE meta.meta_key LIKE 'req_img_%'
I got this result:
+------------+----+-----------------------+
| category | ID | image |
+------------+----+-----------------------+
| 1 | 3 | a1c8f69edb37bf6c6.jpg |
| 1 | 4 | a71f4160d7f7f7555.jpg |
| 6 | 6 | 854b9faaa53d8fe02.jpg |
+------------+----+-----------------------+
But I wanted to make enhancement, so I would get only result, where each category has only one image against, for example category 1, has image a71f4160d7f7f7555.jpg and category 6 has image 854b9faaa53d8fe02.jpg
I bet, that I miss some basic knowledge, and simple enhancement with subquery and selecting MAX would work as a charm.
Thanks!
SQL Fiddle
select
category,
(select request_id
from d_requests_meta
where umeta_id = s.ID
) as ID,
(select meta_value
from d_requests_meta
where umeta_id = s.ID
) AS image
from (
SELECT cat.cat_parent AS category, max(meta.umeta_id) ID
FROM d_requests req
LEFT JOIN d_requests_meta meta ON ( req.ID = meta.request_id )
LEFT JOIN d_categories cat ON ( req.req_parent = cat.ID )
WHERE meta.meta_key LIKE 'req_img_%'
group by cat.cat_parent
) s
SELECT category, ID, image
FROM ( SELECT cat.cat_parent AS category, req.ID, meta.meta_value AS image
FROM d_requests AS req
LEFT JOIN d_requests_meta AS meta
ON req.ID = meta.request_id
LEFT JOIN d_categories AS cat
ON req.req_parent = cat.ID
WHERE meta.meta_key LIKE 'req_img_%'
ORDER BY req.ID DESC) AS h
GROUP BY category
I edited Clodoaldo's answer with the use of the inofficial MySQL assumption that GROUP BY will return the 1st row based on ORDER BY in subquery.
Try this http://sqlfiddle.com/#!2/bfe9a/19
SELECT Category, ID, Image FROM (
SELECT Category, ID, Image,
#id:=CASE WHEN #category <> category THEN 1 ELSE #id+1 END AS ImgRank,
#category:=category AS categoryTemp FROM
(SELECT #id:= 0) AS i,
(SELECT #category:= 0) AS c,
(
SELECT cat.cat_parent AS category, req.ID, meta.meta_value AS image
FROM d_requests req
LEFT JOIN d_requests_meta meta ON ( req.ID = meta.request_id )
LEFT JOIN d_categories cat ON ( req.req_parent = cat.ID )
WHERE meta.meta_key LIKE 'req_img_%'
ORDER BY cat.cat_parent, req.id desc
) Vw
) vw2 WHERE IMGRANK = 1