I`ve a problem with grouping and displaying results from two tables in MySQL db.
I`m trying to make a shopping cart, and I have realised a shop menu which contains products categories and via jquery when category is clicked, menu expands and show products from that category.
Example of categories table:
+------------+
| ID | NAME |
+------------+
| 1 | name1 |
| 2 | name2 |
| 3 | name3 |
| 4 | name4 |
+------------+
Example of products table:
+------------------------------------------------------------------------+
| ID | PR_CODE | NAME | DIMENSIONS | COLORS | OFFER | PRICE | CATEGORY |
+------------------------------------------------------------------------+
| 1 | pr_code1 | prod1 | 40 x 40 | blue | 1 | 11.00 | 1 |
| 2 | pr_code2 | prod2 | 120 x 120 | white | 1 | 12.00 | 1 |
| 3 | pr_code3 | prod3 | 60 x 120 | yellow | 0 | 13.00 | 2 |
| 4 | pr_code4 | prod4 | 40 x 60 | orange | 0 | 14.00 | 3 |
+------------------------------------------------------------------------+
Category row in products table telling which category product belongs.
Code for menu:
<div class="shopMenu">
<ul>
<?php
$sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category ORDER BY categories.id DESC";
$query = execute_select($sql);
foreach($query as $row) {
$id = $row['id'];
$name = $row['name'];
$prodId = $row['prodId'];
$prodName = $row['prodName'];
echo '<li><a href="#">' . $name . '</a>
<ul>
<li><a href="&id=' . $prodId . '">' . $prodName . '</a></li>
</ul>
</li>'; }
?>
</ul>
</div>
This code works, but works wrong. If I put sql query as:
$sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category ORDER BY categories.id DESC";
Result is: all categories are listed but categories which contains more products are repeated in main menu as many times as there are products in this category.
If I put sql query as:
$sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category GROUP BY categories.id ORDER BY categories.id DESC";
Result is: all categories are listed once, but only one of products from chosen category is listed, missing other products.
Can somebody help with this query?
There is basically two ways of doing what you want, both of them have been explained in the other response and in the comments. I will try to expand a little about them in this answer.
One query
You retrieve all the data in one query and then use PHP to do some transformation in order to show them in the appropriate way.
There’s two approaches to do that. You can modify the data first and then loop on them to display the menu or you can do everything in just one loop.
Two steps variant
First, we create an array containing the data in a more “usable” way for us, and then we display the content of this new array :
One step variant
We store the current category, and when the category changes, we close the current list and open a new one :
n+1 query
In this solution, we retrieve the list of categories, and then, for each one, retrieves the list of products :
Dry coding warning
I dry coded the preceding piece of PHP code, I’m not even sure I didn’t made some kind of silly mistakes. It is possible you will have to adapt them to your needs. If something is wrong, please point it out in the comments, I will fix it ASAP 🙂
Conclusion
The first two possibilities executes only one query and then parse the results to display meaningful information. The code is, in my opinion, fairly hard to understand and error prone.
The last possibility is much more clearer and, I think, easier to modify and extend.
From a performance point of view, we have only one query in the first two versions, but we retrieves much more data and a join in necessary. I think there’s no easy answer about which solution is best, it will greatly depends on the number of categories and products for each of them. I think the best to do is to test each of the solution on various data set to determine the quicker one.
If I would have to develop this kind of menu, I will personally use the n+1 query approach. Even if the performance are slightly off (which I’m not sure), the solution is so much clearer that it compensates the weaknesses.
Disclaimer
Kudos to every other poster on this question, I didn’t provide a “new” answer, I just put the already provided one in PHP code. Hope this will help !