Sorted output of a joined MySQL result in PHP - php

Doing an allnighter on a project and my mind is blank atm... Simple question really:
I have two MySQL tables, product and category. Each product belongs to exactly one category. Every category has several products.
SELECT
p.uid as product_uid, p.name_NL as product_name, p.price as product_price,
c.uid as category_uid, c.name_NL as category_name
FROM
product p, category c
WHERE
p.category_uid = c.uid
This gives me a nice overview of all products in their respective category. My question is about outputting this data on the page. I'm aiming for this:
<h1>Category name</h1>
<p>Product in this category</p>
<p>Other product in this category</p>
<h1>Next category</h1>
<p>Product in next category</p>
My mind is completely blank right now. How would one go about doing this?
I would like to avoid doing subqueries (if possible).
Kind regards,
M

What about adding ORDER BY category_uid so that the products are ordered by category in your SQL query. Then using PHP, loop through each product (row) and when you encounter a new category, add a new header.
Example:
<?php
// ...
$previous_category_uid = null;
// Loop through each row.
while ( $row = $result->fetch_assoc() )
{
// If the category UID is not the same as the one from the previous row, add a header.
if ( $previous_category_uid != $row['category_uid'] )
{
echo '<h1>' . $row['category_name'] . '</h1>';
$previous_category_uid = $row['category_uid'];
}
}
The benefit of this method is that you don't have to nest queries. A single query will suffice.

Don't you just need to use a GROUP BY category_uid ?

Generally speaking you have two options:
Get all the data at once (like you are doing currently) then use PHP to either pre-sort the data by category. Then do your output looping over this array. So 1 query, 2 + n loops (where n is the number of categories).
Get all your categories and then loop over those for output. In each iteration you will need to query all products for that loop. So 1 + n queries, 1 + n loops (where, again, n is the number of categories).
Option 2 might be more straightforward, but clearly there are more queries. In the end, it's your call.

Assuming you're looking for alphabetical ordering for category names and products within each category:
SELECT
p.uid as product_uid, p.name_NL as product_name, p.price as product_price,
c.uid as category_uid, c.name_NL as category_name
FROM product p INNER JOIN category c ON p.category_uid = c.uid
ORDER BY category_name, product_name
This also converts your query's Cartesian product and WHERE to an inner join.
To output with the headers you want, just loop over the returned rows, and keep track of the category you're in. Whenever the category of the current row is different from the previous one, you print a new h1 for the new category and update the stored "current" category.

Related

Inner Join with PHP

I checked through a few different questions previously asked but they were more advanced than what I need at the moment. I need a simple way to join two tables and display the results so that I can then manipulate them in any way I want once it is collecting the data the way I need it to. The code below is very simple... Yet I am having trouble. First I create a class that connects to the database then I created a method to query the database and join to tables based on common columns. After that I would like the loop to go through the top four results based on their title name which are 'gold', 'silver', 'platinum', 'palladium' I just want to make sure that the join request is working. Please view the code below and maybe you can tell me why the results I keep getting are
1 Gold
1 Gold
1 Gold
1 Gold
Literally I get Gold 4 times when I need a list of all 4 precious metals.I thought that when the while loop runs through I would get each one as it is supposed to run through all 4 rows and there are no more yet it runs through the same 1st row and brings back 1 Gold every time. Both the id and the metals title name. If I am missing something please feel free to ask and I will add it for you if it helps.
class testJoin{
public function __construct($dbCon){
$this->dbConnection = $dbCon;
}
function testingJoin($dbCon) {
if($results = $this->dbConnection->query("SELECT metal.id, metal.title, price.metalId FROM metal INNER JOIN price ON metal.id = price.metalId ORDER BY metal.title LIMIT 0,4")){
while($data = $results->fetch_assoc()){
printf("<p style=\"display:inline;\">%s</p>
<p style=\"display:inline;\">%s</p><br />", $data['id'], $data['title']);
}
}
$dbCon->close();
}
}
JOIN creates a cross-product of the matching rows in the two tables. If there are multiple price rows for each metal, you'll get all those different prices, and then you take the first 4 rows of this.
If you want to limit the number of metals, but not the total number of rows, you can join with a subquery:
SELECT metal.id, metal.title, price.metalId
FROM (SELECT id, title
FROM metal
ORDER BY title
LIMIT 4) AS metal
JOIN price ON metal.id = price.metalId
Or if you want to get just one row per metal, you can use GROUP BY
SELECT metal.id, metal.title, price.metalId
FROM metal
JOIN price ON metal.id = price.metalId
GROUP BY metal.id
ORDER BY metal.title
LIMIT 4
Here you have no reason to join to prices table at all
SELECT metal.id, metal.title
FROM metal
ORDER BY metal.title
Cos u added to result nothing from there.
If u really need join to prices and display results by "not repeated" metal names, u should just GROUP results
SELECT metal.id, metal.title
FROM metal
INNER JOIN price ON (metal.id = price.metalId)
GROUP BY metal.id
ORDER BY metal.title
After that you can retrieve some useful data from prices table, for example average price for each metal
SELECT metal.id, metal.title, AVG(price.price) AS metal_price
FROM metal
INNER JOIN price ON (metal.id = price.metalId)
GROUP BY metal.id
ORDER BY metal.title
Also you should understand difference between LEFT JOIN and INNER JOIN.
LEFT - will fetch ALL needed rows from first table (metal) and add results from second (prices) even if there is no such metal in prices table (then results from second table will be NULL). (metal.id = price.metalId) can be understanded as "ALL metals with some prices, if they have"
INNER - will fetch ONLY those rows from first table which are presented in second table, by "JOIN ON" condition. (metal.id = price.metalId) can be understanded as "THOSE metals WHICH HAVE prices"
https://pp.vk.me/c623725/v623725696/14ae4/459rNGJwMJc.jpg

Run While Loop and check another table for variable before outputting data

I have a while statement. That loops through my "customer" table and outputs the data.
while($row = mysql_fetch_array($result))
{
echo ".$row['category_id'].<br>
.$row['name'].<br>
.$row['address'].<br>
.$row['status'].<br>"
}
This all works fine.
The problem I have is the "category_id" is stored as a number in the "customer" table and the different categories are stored with their unique numbers and associated written names in a seperate "category" table.
For example customer table has,
category id - 150
name - Stackoverflow
address - 123 Main Street
status - Active
For example category table has
id - 150
name - Message Board
As I run my while loop to output the customer data, I need to query the category table to get and display the "name" associated to category_id.
I figured a while loop in a while loop, what do you guys think? I appreciate the help.
You'll want to do a MySQL join and get all the information at once: http://www.tizag.com/mysqlTutorial/mysqljoins.php
Your query will look something like this:
SELECT
c.name,
c.address,
c.status,
ca.name as category_name
FROM
customer c
JOIN
category ca
ON
ca.id = c.category_id
And your PHP will look like:
while($row = mysql_fetch_array($result)) {
echo "$row[category_name]<br>
$row[name]<br>
$row[address]<br>
$row['status']<br>";
}

User "Categories" table as heading for "SubCategories"

I don't know if this is possible at all...
But I need to create an sql query that lists all my Categories with their Sub Categories. Problem is that Categories and Sub Categories are stored in separate tables.
The database structure is as follows: TABLE NAME: "Categories", COLUMNS: "ID, Name"
TABLE NAME: "Sub_Categories". COLUMNS: "ID, Name, Category_ID". The category_ID is used to link each sub category to it's parent category using ID's.
Right now I just have an sql query code that pulls all the "Categories"... how can I make it so that it will show a list of sub categories under each one?
<?php
$catlist= mysql_query('SELECT * FROM categories WHERE hide = 0 ORDER BY `order` ASC')
or die(mysql_error());
?>
<?php foreach ($catlist as $catitem): ?>
<h2 class="category"><?php echo $catitem['name']; ?></h2>
<?php endforeach; ?>
<?php ?>
this will get you the rest of the info - you will then need to do layout and suppress the repeating category text.
SELECT *
FROM categories c, sub_categories s
WHERE c.hide = 0
AND s.category_id = c.category_id
ORDER BY `order` ASC
You could use a join onto the subcategories table. http://dev.mysql.com/doc/refman/5.0/en/join.html
However, I recommend not differentiating between top level and sub-level categories at the table level, but rather put them all in one table with the following fields: parent_id, left_id, and right_id. Basically, this allows an unlimited depth of categories within a hierarchy. It may take a little research into how to properly implement it, but it is more powerful by far.

Mysql - Subqueries w/ Counting Items

I have 2 tables .. categories & deals. Ultimately, I want to do the following:
// foreach category
// Display category title
// show 3 entries in deals where it matches category title above, along with a link saying: "View (count) More Deals
Now, there are a couple of ways for me to do this:
// The "bad" way:
// foreach category
// fetch count of total deals in this category
// Display category title (once)
// Fetch & Display 3 deals
// Display "View (count) more deals
// This is just quite a few queries for one page
The "other" way:
SELECT count(deals.id),
category.name as category_name,
deal.name as deal_name
FROM $db[ddd_deals] as deals
JOIN $db[ddd_categories]
ON $db[ddd_categories].id = $db[ddd_deals].category_id
WHERE deals.city_id = '$_SESSION[city_id]'
ORDER BY $db[ddd_categories].name
The above does everything except display the number of deals in each category, and only display 3 deals per category
-Should I be selecting FROM the deals table (or from the categories table, then grab the deals?)
// I also tried the following. It returns what category and deal, but the count is off (it is only displaying 1):
SELECT count(deals.id) as cc,
cat.name as cat_name,
deals.name as name
FROM ddd_categories as cat
JOIN ddd_deals as deals
ON deals.category_id=cat.id
GROUP BY deals.id
ORDER BY cat.id
Just a quick one, whether you group by deals.id or .., the order by came at an incorrect place.
select cc, cat_name, name from (
SELECT count(deals.id) as cc, cat.name as cat_name, deals.name as name
FROM ddd_categories as cat
JOIN ddd_deals as deals on deals.category_id=cat.id
GROUP BY deals.id
)
ORDER BY xxx;
You need to GROUP BY cat.id instead of deals.id. Try to following:
SELECT count(deals.id) as cc, cat.name as cat_name, deals.name as name
FROM ddd_categories as cat JOIN ddd_deals as deals ON deals.category_id=cat.id
GROUP BY cat.id

SQL/PHP: Order by highest vote from different table

I needed to recommend to the visitor similar items to the one they where looking at. So I asked in another question for a SQL query to get items with the same categories (from a 'categories' table). The next is the correct code I'm actually using:
SELECT c2.itemID
FROM categories c1
JOIN categories c2 ON c1.catID = c2.catID
WHERE c1.itemID = :id
AND c2.itemID <> :id
GROUP BY c2.itemID
ORDER BY count(c2.itemID) DESC;
It returns items ordered by the number of categories matches (from a table containing 'itemIDs' and 'catIDs'). So for example: item 1 have categories 2,3,4,5,6 and item 2 have categories 2,3,4,5,6 and item 3 have categories 3,5,6 then if I compare item 1 (the one the visitor is looking at) to item 2 and 3 I need to get item 2 first and then item 3 because item 2 have more categories matches than item 3.
NOW: I need also to to order the results by highest vote. The items votes are in a different table. The table 'votes' contain two columns: 'itemID' and 'total_value', 'total_value' being the final rate.
So, How to order the results also by the number of categories matches and also by highest vote from a different table???
Update: The number if items in the table is 8000+. I think the better thing to do to recommend the most similar items is to order it first by items with the exact set of categories and then by votes. Any ideas? Thanks!
SELECT c2.itemID
FROM categories c1
JOIN categories c2 ON c1.catID = c2.catID
JOIN votes v ON v.itemID=c1.itemId
WHERE c1.itemID = :id
AND c2.itemID <> :id
GROUP BY c2.itemID
ORDER BY count(c2.itemID), v.total_value DESC;

Categories