MySQL select parents and child all from on table - php

I have one table that holds parent pages and sub pages, where top page is parent_id = 0 and subpage parent_id is the id of as other page.
ID 1 | parent_id 0
ID 2 | parent_id 0
ID 3 | Parent_id 1
ID 4 | parent_id 1
ID 5 | parent_id 0
So I have
SELECT *
FROM pages AS parent
WHERE parent.parent_id = 0
SELECT *
FROM pages AS sub
WHERE sub.parent_id != 0
The above separate query's work. But i'm sure there is a may to list all pages with one query.
I'm trying to get a list similar to this, grouping sub pages to the parent page
Main (Title1)
Main (Title2)
SUB (Title1)
SUB (Tible2)
Main (Title3)
etc
I've been trying my luck with inner join and left join but I can't solve this.
I has hoping a kind soul with some experience will give me a hand. :)
Thanks

Try following query, It has additional column page_type which indicates page type.
SELECT page.* , IF(
(SELECT p.parent_id FROM pages p WHERE p.id = page.id) = 0, "PARENT", "CHILD"
) AS PAGE_TYPE
FROM pages AS page

You can use a null check and a LEFT JOIN to itself:
SELECT IF(parent.ID IS NULL, 'MAIN', 'SUB') as grouping, page.*, parent.*
FROM pages as page
LEFT JOIN pages as parent
ON page.parent_id = parent.ID

Related

MySQL/MariaDB recursive statement, Return empty if children do not match condition

I have tables like this in the database:
Category
id_category
id_parent
level_depth
1
0
0
2
1
1
20
2
2
21
2
2
22
2
2
30
22
3
category_product
id_category
id_product
2
200
2
201
2
202
20
202
20
203
20
204
I have this statement that deletes the categories that have no products.
Db::getInstance()->execute('UPDATE '._DB_PREFIX_.'category SET `active` = 0 WHERE `id_category` NOT IN(SELECT `id_category` FROM '._DB_PREFIX_.'category_product)');
But this doesn't work for me because I don't want to delete parent categories if the child categories do have products.
I would like to get the categories that have no products where all of their children also have no products.
I've been experimenting with recursive select statements, but I would need it to return nothing in case any children have a product. This query is not correct because it returns all the categories that do not have any product and also all the children that do not have a product.
with recursive decendents as (
-- Category that has no Products
Select c.id_category
FROM category c
WHERE c.id_category NOT IN(SELECT id_category
FROM category_product)
join all
-- child categories
SELECT c.id_category
FROM category c, descendants d
WHERE c.id_parent = d.id_category AND c.id_category NOT IN(SELECT id_category
FROM category_product)
)
SELECT id_category From descendants
Any idea how I can get this? either with recursive query or not.
Thanks.
In mysql you can write update statement as follows
UPDATE category c JOIN category_product cp ON cp.`id_category` = c.`id_category` SET c.`active` = 0
and if your execute it from a tool make sure
sql_safe_updates
set o
as we are updating with out primary key

MySQL count child elements

I have a categories table:
id | name | parent_id
1 | Camaro | 0
2 | Chevelle | 0
3 | Sale - Camaro Parts | 1
4 | Bestselling Parts | 1
My first request looks like:
'SELECT
*
FROM
`categories`
WHERE
parent_id = :parent_id';
And after I'm fetching result set I make sub query to check if row has child elements:
foreach($result as $r) {
$r->hasChild = count(ORM::forTable('categories')->where('parent_id', $r->id)->findArray());
$data[] = $r;
}
Is any way to avoid multiple connection to DB in foreach loop and get data in first query?
Thanks!
This isn't awful to do, so long as you only want the count of children below the selected rows. If you want the entire hierarchy, you'll need to use a better RDMS.
The main part of the solution here is self joining the same table. Then we can use the count() aggregate function to see how many children are attached to each item.
select
categories.id
, categories.name
, categories.parent_id
, count(chld.id)
from
categories
left join categories chld
on categories.id = chld.parent_id
where
parent_id = :parent_id
group by
categories.id
, categories.name
, categories.parent_id
You can do a self join to the table on parent_id and id. Based on whether you want categories with child or not you can do a left join or inner join. Kind of a similar question is mentioned here -
Mysql Self Join to find a parent child relationship in the same table

MySQL: Select Category and (optional) Sub-Category for Projects in One Statement

In my MySQL-Database, I have two tables, one for the projects and one for the categories:
The projects-table looks something like this:
id | project_title | category_id
---|----------------------|------------
1 | My Book Project | 101
2 | My Comic Project | 102
3 | My Magazine Project | 104
Then I have the categories. These can have a parent category which is stored in the same table:
id | category_title | parent_id
---|--------------------|----------
101 | Books | 0
102 | Comics | 101
103 | DVDs | 0
104 | Magazines | 101
I like to fetch all the projects with the according category and (optional) sub-category (if parent_id is provided). If the category has no sub-category, the sub-category should be something like "-" or "none".
Now I know how I can get all these values with several statements:
First Statement: Fetch the projects with the indicated category (which can be a main-category or a sub category, therefor I fetch also the category's parent_id):
SELECT
p.project_title,
c.category_title,
c.parent_id as cat_parent_id
FROM
projects p,
categories c
WHERE
p.category_id = c.id
Second Statement(s): After that, I could fetch the possible sub-category of a project within a loop, or assign the found category as main-category, e.g. with php:
<?php
foreach( $rows as $project ) {
if ( $project['cat_parent_id'] > 0 ) {
$project['sub_category'] = $project['category_title'];
// query main-category here
}
else {
$project['main_category'] = $project['category_title'];
$project['sub_category'] = 'none';
}
// do things with this project ...
}
?>
The problem is that I will have another query for each project found, and this is not nice. I think there should be a way to fetch all the required values in one statement. I found this question on SO which is nearly the same, but in my case, the sub-category is obtional.
As John Cleese would say: Could someone please give me a push?
You can join categories twice:
SELECT
p.project_title,
c.category_title,
COALESCE(c0.category_title, '-')
FROM projects p join categories c on p.category_id = c.id
left join categories c0 on c.parent_id = c0.id;
But it will work only for two levels hierarchy (any category may have zero or one parent).
Accoding to the comments:
SELECT
p.project_title,
COALESCE(c0.category_title, c.category_title),
case when c0.id is not null then c.category_title else '-' end
FROM projects p join categories c on p.category_id = c.id
left join categories c0 on c.parent_id = c0.id;
SELECT
p.project_title,
IF(c2.category_title is null,
c1.category_title,
c2.category_title) as main_category,
IF(c2.category_title is null,
"none",
c1.category_title) as sub_category
FROM projects p
LEFT JOIN categories c1 ON (c1.id = p.category_id)
LEFT JOIN categories c2 ON (c2.id = c1.parent_id)
http://sqlfiddle.com/#!2/85020/1
This returns the requested result:
PROJECT_TITLE MAIN_CATEGORY SUB_CATEGORY
My Book Project Books none
My Comic Project Books Comics
My Magazine Project Books Magazines

SQL select all using JOIN

I'm using this query to collate two sets of results but I now need to use JOIN instead of UNION to get the second part of the data from another table.
However I need quite a lot of fields and can't seem to find a way to maintain the use of SELECT * when using JOIN.
mysql_query("SELECT * FROM table.products WHERE category='$cat' GROUP BY product_id ORDER BY id UNION ALL SELECT * FROM table.products WHERE type='red' GROUP BY product_id ");
Table - products
product_id | title | category | id
0 one home 10
1 two home 11
1 two - a home 12
2 three work 13
Table - product_details
product_id | type | size |
0 blue S
1 blue M
1 red L
Ultimately I need to list every product in the first table for a given category e.g home,
as there is sometimes two entries or more for a single product id, I need to only select one row for each product id value. I also need to join the second table so I can get the size info, however I must be able to get the size info by preferring a type e.g red.
So for this example I would get a list like:
product_id | title | category | type | size
0 one home blue S
1 two home red L
This excludes product_id 2 as it's not in the home category, the first entry for product_id equaling 1 is selected because of the GROUP BY and ORDER BY and the information on size for product_id 1 is L because it is of type red not blue.
Assuming you are using MySQL, you want a join with an aggregation or aggressive filtering. Here is an example using join and aggregation:
select p.product_id, p.title, p.category,
substring_index(group_concat(pd.type order by pd.type = 'red' desc, pd.type), ',', 1) as type,
substring_index(group_concat(pd.size order by pd.type = 'red' desc, pd.type), ',', 1) as size
from products p join
product_details pd
on p.product_id = qpd.product_id
where p.category = 'home'
group by p.product_id;
The expression substring_index(group_concat(. . .)) is choosing one type (and one size) with precedence given to the red type.
Your query can be simplified like below since you are using the same table table.products. Not sure why you need to UNION them.
SELECT * FROM table.products
WHERE category='$cat'
and type='red'
GROUP BY product_id
EDIT:
With your edited post, the query should look like
select p.product_id,p.title,p.category,q.type,q.size
from products p join product_details q
on p.product_id = q.product_id
where p.category = 'home'
and q.type = 'red'

MySQL Order by, then Update Inner Join

I have a table categories which contains the columns ID & ParentID I would like to add a field called Level, which states which level in the category tree each category is.
I think i found my solution but it is in sql not mysql. So i have been converting it to the correct syntax. However, i think im missing a step. So, here is my code:
ALTER TABLE categories DROP Level;
ALTER TABLE categories ADD Level INT NULL;
UPDATE categories
SET Level = 0
WHERE ParentID IS NULL;
UPDATE categories AS A
INNER JOIN categories B ON A.ParentID = B.ID
SET A.Level = B.Level + 1
WHERE A.Level IS NULL AND
B.Level IS NOT NULL;
I think the problem may lie in the fact that in my DB The order of the categories do not come in any specific order, what i mean is as follows:
ID ParentID
2 NULL 0
4 55
7 2
.....more categories
55 2
So what i would like it do do is:
ID Parent Level
2 NULL 0
3 55 2
7 2 1
....
55 2 1
However, i think, but i might be wrong, is that i need to either order by ParentID first before i do the last operation, or my query is missformed.
I am not getting any errors however, but just not getting the results i am expecting this is what im getting;
ID Parent Level
2 NULL 0
3 55 NULL
7 2 1
....
55 2 1
Any ideas?
The question is how many levels do u have?
If they are 3 levels than u can do it like this
ALTER TABLE categories DROP Level;
ALTER TABLE categories ADD Level INT NULL;
UPDATE categories SET Level = 0 WHERE ParentID IS NULL;
UPDATE categories SET level = 1 where parentID = 2;
UPDATE categories SET level = 2 where parentID > 2;

Categories