PHP/MySQL group results by column - php

in order to keep as few SQL statements as possible, I want to do select set from MySQL:
SELECT * FROM products WHERE category IN (10,120,150,500) ORDER BY category,id;
Now, I have list of products in following manner:
CATEGORY
- product 1
- product 2
CATEGORY 2
- product 37
...
What's the best and most efficent way to process MySQL result?
I thought something like (pseudo PHP)
foreach ($product = fetch__assoc($result)){
$products[$category][] = $product;
}
and then when outputting it, do foreach loop:
foreach($categories as $category){
foreach($products[$category] as $product){
$output;
}
}
Is this the best, or is something magical like mysql_use_groupby or something?

Like mluebke commented, using GROUP means that you only get one result for each category. Based on the list you gave as an example, I think you want something like this:
$sql = "SELECT * FROM products WHERE category IN (10,120,150,500) GROUP BY category ORDER BY category, id";
$res = mysql_query($sql);
$list = array();
while ($r = mysql_fetch_object($res)) {
$list[$r->category][$r->id]['name'] = $r->name;
$list[$r->category][$r->id]['whatever'] = $r->whatever;
// etc
}
And then loop through the array. Example:
foreach ($list as $category => $products) {
echo '<h1>' . $category . '</h1>';
foreach ($products as $productId => $productInfo) {
echo 'Product ' . $productId . ': ' . $productInfo['name'];
// etc
}
}

Nope, I think your solution is the best for this problem. It seems that what's important for you is the output later on, so you should stick with your approach.

Do you want to get a list of categories or actually get all products grouped into categories?
If it's the latter, best to do:
SELECT
p.product_id,
p.name,
p.category_id,
c.name AS category
FROM products p
JOIN categories c ON (c.category_id = p.category_id AND p.category_id IN (x,y,z))
Then in PHP you can go through the array (psuedo code):
$cats = array();
foreach($products as $product) {
if(!in_array($product['category'], $cats)) {
$cats[$product['category_id']] = $product['category'];
}
$cats[$product['category_id']][$product['product_id']] = $product['name'];
}
Which will leave you with $cats as an array with products sorted into it.

Related

MySQL group result [duplicate]

in order to keep as few SQL statements as possible, I want to do select set from MySQL:
SELECT * FROM products WHERE category IN (10,120,150,500) ORDER BY category,id;
Now, I have list of products in following manner:
CATEGORY
- product 1
- product 2
CATEGORY 2
- product 37
...
What's the best and most efficent way to process MySQL result?
I thought something like (pseudo PHP)
foreach ($product = fetch__assoc($result)){
$products[$category][] = $product;
}
and then when outputting it, do foreach loop:
foreach($categories as $category){
foreach($products[$category] as $product){
$output;
}
}
Is this the best, or is something magical like mysql_use_groupby or something?
Like mluebke commented, using GROUP means that you only get one result for each category. Based on the list you gave as an example, I think you want something like this:
$sql = "SELECT * FROM products WHERE category IN (10,120,150,500) GROUP BY category ORDER BY category, id";
$res = mysql_query($sql);
$list = array();
while ($r = mysql_fetch_object($res)) {
$list[$r->category][$r->id]['name'] = $r->name;
$list[$r->category][$r->id]['whatever'] = $r->whatever;
// etc
}
And then loop through the array. Example:
foreach ($list as $category => $products) {
echo '<h1>' . $category . '</h1>';
foreach ($products as $productId => $productInfo) {
echo 'Product ' . $productId . ': ' . $productInfo['name'];
// etc
}
}
Nope, I think your solution is the best for this problem. It seems that what's important for you is the output later on, so you should stick with your approach.
Do you want to get a list of categories or actually get all products grouped into categories?
If it's the latter, best to do:
SELECT
p.product_id,
p.name,
p.category_id,
c.name AS category
FROM products p
JOIN categories c ON (c.category_id = p.category_id AND p.category_id IN (x,y,z))
Then in PHP you can go through the array (psuedo code):
$cats = array();
foreach($products as $product) {
if(!in_array($product['category'], $cats)) {
$cats[$product['category_id']] = $product['category'];
}
$cats[$product['category_id']][$product['product_id']] = $product['name'];
}
Which will leave you with $cats as an array with products sorted into it.

Storing categories and subcategories with the possibility of infinite sub-'s, and a function to return them all from MYSQL into JSON

My current function:
$categories = array();
$db -> new PDO(connect);
foreach($db->query('SELECT * FROM categories') as $row) {
$category = array();
array_push($category, $row['categoryID']);
foreach($db->query("SELECT * FROM subcategories WHERE categoryID = {$row['categoryID']} ") as $row2)
{
if ($row2["subcatparentID"] == null) {
$category[$row['categoryID']][] = $row2['subcatID'];
}
//Here I need to get all the subcats with parent subcats, and
//then I need to add the subcategory to $category finding the parent subcategory
//mentioned before. So, I need a infinite multi-dimensional search function or something..
//Plus, I need to use a repeating while loop to make sure all subcategories with
//parents are added into the array, because if 14 is linked to 11 and 11 is linked to
}
$categories[] = $category;
}
return json_encode($categories);
That's my only idea, currently. How do I find the parent subcategory if it can be in any dimension? For example, subcategory 11 is linked to 12, 12 is linked to 14, 14 is linked to 13, I will have $category[13][14][12][11]
My table structure is like this:
table "subcategories": subcatID, categoryID, name, subcatparentID
table "categories": categoryID, name //these are main categories
in return with JSON format I would like something like this:
{"1":[41[43,42[45, 44]]],"2":[26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"3":[56,57,58,59,60,61],"4":[62,63,64],"5":[1,2,3,4,5,6,21[22,23]]}
although that doesn't seem to work with JSON.parse, strangely enough.
Idea for a recursive way of doing this:-
<?php
$categories = array();
$db -> new PDO(connect);
foreach($db->query('SELECT * FROM categories') as $row)
{
$categories[$row['categoryID']] = get_sub_categories($db, $row['categoryID']);
}
return json_encode($categories);
function get_sub_categories($db, $parent_category)
{
$return_categories = array();
foreach($db->query("SELECT * FROM subcategories WHERE categoryID = $parent_category ") as $row2)
{
$return_categories[$row2['subcatID']] = get_sub_categories($db, $row2['subcatID']);
}
return $return_categories;
}
?>
Not tested, but this is to give you an array where the key is the category and the value is an array of catgories under that category (which in turn could each be arrays, etc)
EDIT - Still not 100%, but doing this with a loop around but starting at the bottom of the tree, using array_unshift to prepend the sub categories to the array for each category.
This is relying on subcatparentID being set to 0 when a sub category is the highest one under that category, but if something else that is easy to change.
Not tested
<?php
$categories = array();
$db -> new PDO(connect);
$categories = array();
foreach($db->query('SELECT categories.categoryID, a.subcatID, a.subcatparentID
FROM categories
INNER JOIN subcategories a ON categories.categoryID = a.categoryID
LEFT OUTER JOIN subcategories b ON a.categoryID = b.categoryID AND a.subcatID = b.subcatparentID
WHERE b.subcatID IS NULL
') as $row)
{
$categories[$row['categoryID']] = array();
$parent = $row['subcatparentID']
while($parent != 0)
{
// following should only get a single row
foreach($db->query("SELECT subcatID, subcatparentID FROM subcategories WHERE categoryID = $row['categoryID'] AND subcatID = $parent") as $row2)
{
array_unshift($categories[$row['categoryID']], $row2['subcatID']);
$parent = $row2['subcatparentID']
}
}
}
?>

Display all pages under categories in PHP with joins

I have 2 tables. The first one, pages, contains a foreign key category_id and the other table, categories, contains an id and a title field.
Here is my sql query :
SELECT categories.title as titleCategory, pages.title as titlePage FROM categories INNER JOIN pages ON pages.category_id = categories.id ORDER BY categories.title
What is want is to display the pages and categories like this :
category 1
page 1
page 2
category 2
page 1
etc...
Is there a way to do that using a foreach or do I have to use 2 queries?
Thank you very much for your help.
Assuming PHP, but the logic shoudl work in any language.
$cur_cat = null;
foreach($results as $result){
if($cur_cat != $result['titleCategory']){
echo $result['titleCategory'];
}
$cur_cat = $result['titleCategory'];
echo $result['titlePage'];
}
Just put a check in the single foreach loop.
$currentCategory = null;
foreach( $results as $result ) {
if( $currentCategory != $result['titleCategory'] ) {
$currentCategory = $result['titleCategory'];
echo $result['titleCategory'];
}
echo $result['titlePage'];
}
This will only print out the title if it has changed. You already have the, ordered by categoryTitle so if the category doesn't change it will only print the pageTitle.

Building a Mysql table-tree using query instead of loops in PHP

I have a category tree that contains up to 3 levels of children-categories, like this:
houseproducts->livingroom->sofas->twoseats
houseproducts->livingroom->sofas->threeseats
houseproducts->livingroom->sofas->fourseats
So for each sublevel, I do a SELECT based on the mothers category-id. This is done in a PHP-loop like the code below, but I guess it could be done in one single Mysql-query, for better performance. I have tried different JOINS but find it really difficualt. Any advice will be highly appreciated.
function build_category_tree()
{
$cat = array();
// main category loop
$r1 = mysql_query("SELECT cat_id,cat_name FROM categories WHERE cat_mother=0 OR cat_mother='' ORDER BY cat_name");
while ($row=mysql_fetch_assoc($r1))
{
$cat[$row['cat_id']] = $row['cat_name'];
// check for subcategories
$r2 = mysql_query("SELECT cat_id,cat_name FROM categories WHERE cat_mother='".$row['cat_id']."'");
while ($subrow=mysql_fetch_assoc($r2))
{
$cat[$subrow['cat_id']] = ' - '.$subrow['cat_name'];
// check if there is subcats for the current subcategory
$r3 = mysql_query("SELECT cat_id,cat_name FROM categories WHERE cat_mother='".$subrow['cat_id']."'");
while ($subrow2=mysql_fetch_assoc($r3))
{
$cat[$subrow2['cat_id']] = ' -- '.$subrow2['cat_name'];
// check if there is subcats for the current subcategory
$r4 = mysql_query("SELECT cat_id,cat_name FROM categories WHERE cat_mother='".$subrow2['cat_id']."'");
while ($subrow3=mysql_fetch_assoc($r4))
{
$cat[$subrow3['cat_id']] = ' --- '.$subrow3['cat_name'];
}
}
}
}
return $cat;
}
I would read your entire table into an array and segment that array by the mother's keys
Try this:
SELECT l1.cat_id AS l1_cat_id
,l1.cat_name AS l1_cat_name
,l2.cat_id AS l2_cat_id
,l2.cat_name AS l2_cat_name
,l3.cat_id AS l3_cat_id
,l3.cat_name AS l3_cat_name
,l4.cat_id AS l4_cat_id
,l4.cat_name AS l4_cat_name
FROM categories AS l1
JOIN categories AS l2
ON l2.cat_mother = l1.cat_id
JOIN categories AS l3
ON l3.cat_mother = l2.cat_id
JOIN categories AS l4
ON l4.cat_mother = l3.cat_id
WHERE l1.cat_mother=0 OR l1.cat_mother=''
ORDER BY l1_cat_name, l2_cat_name, l3_cat_name, l4_cat_name

PHP Mysql Left Join

<?php
$q = mysql_query("SELECT sub_cat.*, links.*
FROM links
LEFT JOIN sub_cat
ON links.p_id = sub_cat.id
WHERE sub_cat.p_id = '$id'
ORDER BY name ASC") or die (mysql_error());
while ($r = mysql_fetch_assoc($q))
{
$links_name = $r['name'];
$link_h3 = $links_name != '' ? '<h3>' . $links_name . '</h3>' : '';
//print $link_h3;
print '<pre>';
print_r ($r);
}
?>
I have two tables with rows like:
sub_cat
id
name
p_id
links
id
links
p_id
In sub cat i have movie categories, like foreign language movies, national movies, uncategorised movies and so on. In links table i have concrete movie links and depending on sub category.
The only thing is that i do not want dublicate titles (sub_cat.name).
result is:
Without Category www.moviesite.com
Without Category www.moviesite2.com
Without Category www.moviesite3.com
Foreign Movies www.moviesite1.bla
Foreign Movies www.moviesite2.bla
I want to be
Without Category www.moviesite.com
www.moviesite2.com
www.moviesite3.com
Foreign Movies www.moviesite1.bla
www.moviesite2.bla
and do not have any idea how to do this :(
any help appreciated.
To do the job, you have 2 solutions:
The first solution is to process your data before showing it, in order to group all movies by category.
You can do for example:
$moviesByCategory = array();
while ($r = mysql_fetch_assoc($q))
{
// Create the new sub array for the new category if necessary
if (!isset($moviesByCategory[$r['name']]))
$moviesByCategory[$r['name']] = array();
// Add the movie in the category
$moviesByCategory[$r['name']][] = $r['links'];
}
And then, you can now iterate on this new array like
foreach($moviesByCategory as $category => $movies)
{
// Print the category name
echo '<h1>' . $category . '</h1>';
// Print all movies of the category
foreach($movies as $movie)
echo '<h3>' . $movie . '</h3>';
}
The second solution is to modify the SQL query to group directly all movies that have the same category. You just have to use a GROUP BY clause on sub_cat.id and then apply an agregate function on all other fields in the select.
For performance aspect, the best solution is the SQL solution, but doing it with PHP will give you more flexibility for the presentation.
Try something like:
$lastSubcatName = "";
while ($r = mysql_fetch_assoc($q))
{
$links_name = $r['name'];
if($lastSubcatName != $links_name)
{
echo "<h1>".$links_name."</h1>";
$lastSubcatName = $links_name;
}
echo '<h3>' . $r['links'] . '</h3>';
}

Categories