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
Related
I am still a php/mysql newbie and I am working on mysql table relationship concept and i am having an issue with using mysql count in multiple table. Here is my db structure.
**product table**
id product_name product_img groupeid
1 Sneaker Mark sneaker_adi.png 1
2 bag Eric bageric.png 2
3 Sneaker Etoi sneakeretoi.jpg 1
**groupe table**
group_id group_name
1 men
2 women
**category table**
catid catname
1 sneaker-shoes
2 bag-woman
**productcategory table**
prod_id cat_ID
1 1
2 2
3 1
What i want to do is to determine the number of sneaker-shoes using mysql.
We can see that the number of sneaker-shoes in the db is 2.
But how can i use **count()** in these multiple tables.
I tried like this;
$sql = "SELECT COUNT(*) product.id,product_name,catname FROM product INNER JOIN productcategory ON product.id = prod_id INNER JOIN category ON catid = cat_ID WHERE catname='sneaker-shoes'";
i got error like:
Fatal error: Call to a member function execute() on a non-object in C:\wamp\www\kbashopping\Homme\index.php on line 32
Hope i exposed the issue clearly, any help and assistance will be appreciate
Thanks
If you are looking only for the count, mention only the count phrase in the Select clause.
Change :
SELECT COUNT(*) product.id,product_name,catname FROM
to :
SELECT COUNT(product.id) FROM
SELECT count (pc.cat_ID) FROM productcategory pc inner join category c on c.catid = pc.cat_ID where c.catname = 'sneaker shoes';
This will build a temporary table in mysql that joins category and product category but only including results where the catname is sneaker shoes. Then it selects a column to run the count operation on, and returns the result of count.
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
Haven't been able to crack this one for longer than I care to admit.
I don't reckon this could be that unusual a problem - surely mapping table troubles come a dime a dozen in SQL land - so I'll just ask, in the hope that others may benefit from my ignorance.
I'm trying pull together five tables to display the products that are connected to both a main category and a sub category.
What I want to display is the information in the product table.
The tables are as follows:
**products**
product_id (PK) | [other columns with product information]
**categories**
category_id (PK) | category_name
**sub_categories**
sub_category_id (PK) | sub_category_name
**products_and_categories**
pc_rel_id (PK) | pc_product_id (FK - products.product_id) | pc_category_id (FK - categories.category_id)
**products_and_sub_categories**
psc_rel_id (PK) | psc_product_id (FK - products.product_id) | psc_sub_category_id (FK - sub_categories.sub_category_id)
My fruitless attempts so far include:
$getSCatProds = new QueryClass();
$catSubArray['queryCategory'] = 'main category name';
$catSubArray['querySubCategory'] = 'sub category name';
//PDO fetchAll(PDO::FETCH_ASSOC)
$resultArray = $getSCatProds->fetchAllQuery("SELECT product_id FROM products
INNER JOIN (products_and_categories, products_and_sub_categories)
ON (products_and_categories.pc_product_id=products.product_id
AND products_and_sub_categories.psc_product_id=products.product_id)
INNER JOIN (categories, sub_categories)
ON (categories.category_id = products_and_categories.pc_category_id
AND sub_categories.sub_category_id = products_and_sub_categories.psc_sub_category_id)
WHERE categories.category_name = :queryCategory
AND sub_categories.sub_category_name = :querySubCategory", $catSubArray);
print_r($resultArray); //returns an empty array
and:
//Query method and sent information same as in the above query
$resultArray = $getSCatProds->fetchAllQuery("SELECT product_id FROM products
INNER JOIN (products_and_categories, products_and_sub_categories, categories, sub_categories)
ON (products_and_categories.pc_product_id=products.product_id
AND products_and_sub_categories.psc_product_id=products.product_id)
WHERE categories.category_name = :queryCategory
AND sub_categories.sub_category_name = :querySubCategory", $catSubArray);
print_r($resultArray);
//returns a stupendous number of product_id:s, 2030 of them to be exact.
//There are 2075 items in the products table ...
Please, give my poor head a hand with finding the right query.
Thank you.
You need to do a separate join with each table.
SELECT p.product_id
FROM products AS p
JOIN products_and_categories AS pc ON p.product_id = pc.pc_product_id
JOIN categories AS c ON pc.pc_category_id = c.category_id
JOIN products_and_subcategories AS psc ON p.product_id = psc.psc_product_id
JOIN sub_categories AS sc ON psc.psc_sub_category_id = sc.sub_category_id
WHERE c.category_name = :queryCategory
AND sc.sub_category_name = :querySubCategory
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
I have 2 tables:
categories (id, categoryName),
menu (id, menuname, category_id)
I would like to display all categories, which have one or more records in the menu.
And after every categoryName to show 5 menuname.
Is it possibe, to do this in the one recordset?
Thank you!
These are my 2 recordsets:
$query = "select a.id, a.name from categories as a where a.id in (select count(*) from menu as b on b.category_id = a.id)";
$result = mysql_query($query);
while ($row = mysql_fetch_array($result)) {
echo $row['name'];
$category_id = intval($row['id']);
$query = "select menuname from menu where category_id = $category_id limit 0, 5";
$resultmenu = mysql_query($query);
while ($rowmenu = mysql_fetch_array($resultmenu)) {
echo $rowmenu['menuname'];
}
}
As mentioned above, i'm not sure what is meant by "And after every categoryName to show 5 menuname".
But to show a list of all category/menu names in alphabetical order you could use the following:
SELECT C.categoryName,
M.menuname
FROM categories C
INNER JOIN menu M ON M.category_id = C.id
ORDER BY C.categoryName,
M.menuname
Update:
At the moment your first query will only be returning at best one row. The subquery is currently counting the number of menu rows and then this figure is being used to pull a row from the category table, which isn't what you want.
The following query joins onto the menu table to ensure that at least one item exists, and then groups by the category fields to ensure that each item is only returned once:
$query = "select a.id, a.name from categories as a inner join menu as b on b.category_id = a.id group by a.id, a.name"
Update 2
Ah sorry, I understand now. No I don't think it's possible to achieve what you want in a single query. Even if it were possible I wouldn't recommend it. Looking at your code, you only want to print the categoryName once for each set of menu items. If you were able to pull back the categoryName and menuname items in one result set like so:
| categoryName | menuname |
---------------------------
| category1 | menu1 |
| category1 | menu2 |
| category1 | menu3 |
| category2 | menu4 |
When iterating through the results you would need to manually check when the categoryName had changed in order to print it out once for that set of menuname items.