Joining 2 tables is not working correctly - php

Idk what i am doing wrong.I am still new to php and mysqli and wanted to know what I am doing wrong.What I have on my 1st list are name of the food,description and what kind of food they are.On my 2nd list I have the ratings of the foods.
TableImg
So lets say for example I click on korean food all the foods with the cuisine_type = korean will be displayed on my page. When mysqli_fetch_array($query_type) it displays kimchi and ribs but when i replace $query_fetch it only displays the first one which is kimchi.My goal is to get all the food from cuisine_type display correctly and their ratings attached to them.
<?php
if(isset($_GET['type'])){
$type = (string)$_GET['type'];
}
(Displays only 1 of them)-> $query_fetch = mysqli_query($connect,"SELECT
cuisine.*,AVG(ratings.cuisine_rating) AS rt FROM cuisine
LEFT JOIN ratings ON cuisine.dish_name = ratings.cuisine_name WHERE
cuisine_type = '$type'");
(Displays everything)-> $query_type = mysqli_query($connect,"SELECT * FROM cuisine WHERE
cuisine_fetch ='$type'");
while($show = mysqli_fetch_array($query_type)){
echo "
<div class='box'>
<img class='foodimg' src='dish/".$show['dish_img'].".jpg'>
<figcaption class='title'>".$show['dish_name']."</figcaption>
<div class='text'>".$show['text']."</div>
</div>
</div>";
}
?>

What you want is to do your average using GROUPing.
SELECT c.*, avg(r.rating) AS rt FROM cuisine c
LEFT JOIN ratings r
ON c.dish_name = r.dish_name
WHERE c.cuisine_type = 'korean'
GROUP BY dish_name
The code can be tested via SQLFiddle.
You will need to replace where i hard coded korean with your php variable to make it work in your system.
The issue you had was you didn't specify how to group your rows, this caused the server to put them into all one group. Most DBMS would have errored when you tried to select columns not in the grouping (which was no columns) without an aggregate (like avg), though it appears yours does not, it allows you to group this way and simply return the first row for every non aggregate.
NOTE you still may need some kind of null handling for avg(r.rating), since Korean Ribs has no ratings, it will return an average rating of NULL. You can handle this by using IFNULL like this: IFNULL(avg(r.rating),2.5) AS rt. I would suggest using maybe 2.5 if rating is out of 5, so your no rating is the middle number, though you can use 0 or 5 or anything.

Assuming the TableImg is correct, you're not using the same names in the query as in the db.
SELECT
cuisine.*,
AVG(ratings.cuisine_rating) AS rt
FROM cuisine
LEFT JOIN ratings ON
cuisine.dish_name = ratings.cuisine_name
WHERE
cuisine_type = '$type'
Use this instead:
SELECT
cuisine.*,
AVG(ratings.Rating) AS rt
FROM cuisine
LEFT JOIN ratings ON
cuisine.dish_name = ratings.dish_name
WHERE
cuisine_type = '$type'

Related

PHP Query within a while fetch loop

I was wondering if it was possible to run a query inside a while loop which is used to display the content of a SQL table.
Here is the code if I'm not clear enough :
$sql="SELECT * FROM hotels WHERE rooms>0";
$req=$db->query($sql);
while($row=$req->fetch()){
//The second query to check how many place is left
$req2=$db->query('SELECT COUNT(*) FROM people WHERE idhotels='.$row["idhotels"].';');
echo "hey".$req2;
$left_rooms= $row["rooms"] -$req2;
echo '<option value="'.$row["idhotels"].'">'.$row["name_hotel"].' ('.$left_rooms.' rooms left)</option>';
}
What I'm trying to do here, is to display a list of hotels with the number of rooms left. The problem is I have to count how many rooms are taken before displaying the number of rooms left, hence the second request.
My code obviously doesn't work, but I can't figure out why.
Can someone help me ?
Many thanks !
Why not using a join and a group by so you only have one query ?
$sql="SELECT h.idhotels,h.name_hotel,count(*) FROM hotels h inner join people p on h.idhotels = p.idhotels WHERE h.rooms>0 group by h.idhotels,h.name";
while($row=$req->fetch()){
// Here do whatever you want with each row
}
Have you tried to calculate your left rooms in the database with a joined query like:
SELECT rooms - COUNT(*) AS left_rooms FROM hotels h WHERE rooms > 0 JOIN people p ON (p.idhotels = h.idhotels) GROUP BY h.idhotels, h.name ORDER BY left_rooms ASC;

Advanced multiple join for mysql

So I have a query that gives me three numbers, I then want to look up these numbers from another table and get the name and url associated with them.
So I have a products table that i query, and that provides me these three numbers, but as there are 3 numbers, an inner join wouldnt work.
So I am essentially just trying to look up the name and url, of 3 numbers from the same table, and wondering how the most efficient way to achieve this would be.
$qry = mysqli_query($this->con,"SELECT * FROM products WHERE product_id=$this->id");
$row = mysqli_fetch_assoc($qry);
$main = $row['main_id'];
$cat = $row['categories_id'];
$sub = $row['sub_cat_id'];
So I can do a query for each number individually, but then that is a total of 4 queries. I was wondering if this could be done in just one, or possibly even 2.
Hard to say for sure without seeing some table structure and sample data, but something like this may work (where othertable is the name of the other table you are pulling urls and names from):
select p.mail_id,
p.categories_id,
p.sub_cat_id,
o1.url mail_id_url,
o1.name mail_id_name,
o2.url categories_id_url,
o2.name categories_id_name,
o3.url sub_cat_id_url,
o3.name sub_cat_id_name
from products p
join othertable o1 on o1.mail_id = p.mail_id
join othertable o2 on o2.categories_id = p.categories_id
join othertable o3 on o3.sub_cat_id = p.sub_cat_id
where p.product_id = $this->id
This assumes that a record exits in othertable for every id that you are looking up.

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

Searching multiple mysql tables

I currently have this code.
switch($search_topic)
{
case 'Title':
$stmt = $dbh->prepare("SELECT game_id, title, platform_name, author_name, price
FROM games
WHERE title
LIKE :search_term");
break;
case 'Platform':
$stmt = $dbh->prepare("SELECT game_id, title, platform_name, author_name, price
FROM games
INNER JOIN platforms ON games.platform_id = author.platform_id
WHERE platform_name LIKE :search_term");
break;
}
the search_topic variable is coming from a listbox on the previous webpage, the Title search works correctly as its simply just search one MySQL table. The Platform search is being abit difficult as I'm attempting to search multiple tables, If that makes sense.
The games table has a column called "platform_id" this this links to the platform table where the key field links to what platform it is under. So how would I go about getting the "platform_id" from the games table and then link it to the "platforms" so I retieve the "platform_title" and then use that to search against.
Schema: http://sqlfiddle.com/#!2/3d3e3/6
Based off of your fiddle, here's the update for you:
SELECT g.game_id, g.title, p.platform_name, a.author_name, g.price
FROM games g
INNER JOIN platforms p ON p.platform_id = g.platform_id
INNER JOIN author a ON a.author_id = g.author_id
WHERE g.title LIKE 'Skyrim';
You would obviously change WHERE g.title LIKE 'Skyrim'; to whichever field you would like to filter on. So the platform query would be:
SELECT g.game_id, g.title, p.platform_name, a.author_name, g.price
FROM games g
INNER JOIN platforms p ON p.platform_id = g.platform_id
INNER JOIN author a ON a.author_id = g.author_id
WHERE p.platform_name LIKE 'Xbox';
I would also suggest that you change your CHARSET to utf8 instead of latin1, assuming this reflects your production database setup.
SELECT games.game_id,
games.title,
games.platform_name,
games.author_name,
games.price,
games.platform_id,
platforms.platform_id,
platforms.platform_name
FROM games, platforms
WHERE
games.platform_id = platforms.platform_id
AND
platform.platform_name LIKE %:search_term%

How do I echo out a name -only if- specific rows in one table match specific rows in another based on an id?

Here's my problem: I'm making a crafting system for a game, and I already have my database filled with information for resources required to craft items.
Here are what my relevant tables look like:
table #edible_resources
(edible_resource_id, edible_resource_name, hunger_points, degeneration_id)
table #edible_ground
(id, resource, amount, location)
table #req_crafting_edible
(req_crafting_edible_id, edible_resource_id, req_resource_amount, created_item_id)
table #items
(item_id, item_name, degeneration_id, is_grounded, is_stackable, can_equip, can_edit)
What I want to do is -only- echo out the craftable item's name if, and only if -all- required resources (and their required amounts) are on the ground in the location of the character.
I have a query that comes close:
SELECT items.item_name, items.item_id FROM items
INNER JOIN req_crafting_edible
ON req_crafting_edible.created_item_id = items.item_id
INNER JOIN edible_ground
ON edible_ground.resource = req_crafting_edible.edible_resource_id
AND edible_ground.amount >= req_crafting_edible.req_resource_amount
WHERE edible_ground.location = $current_location
GROUP BY items.item_name
ORDER BY items.item_name
But this shows me craftable items regardless if I have ALL the required items in the area. It shows me items as long as I have -one- of their required resources.
Is there a way to only show the name of a craftable item only if I have -all- the required resources (and their amounts) in edible_ground where location = $current_location?
For more information on what I've tried:
$get_char = mysql_query("SELECT current_char FROM accounts WHERE account_id ='".$_SESSION['user_id']."'");
$current_char = mysql_result($get_char, 0, 'current_char');
$get_loc = mysql_query("SELECT current_location FROM characters WHERE character_id = $current_char");
$current_location = mysql_result($get_loc, 0, 'current_location');
//---------------------------------------------------------------COOKED FOOD
$get_food = mysql_query("SELECT items.item_name, items.item_id FROM items
INNER JOIN req_crafting_edible
ON req_crafting_edible.created_item_id = items.item_id
INNER JOIN edible_ground
ON edible_ground.resource = req_crafting_edible.edible_resource_id
AND edible_ground.amount >= req_crafting_edible.req_resource_amount
WHERE edible_ground.location = $current_location
GROUP BY items.item_name
ORDER BY items.item_name");
while ($food = mysql_fetch_array($get_food)){
echo $food['item_name'].'<br>';
}
This returns:
Baked Fish
Charred Fish
Fish Soup
Glazed Berry
Cake
Grilled Fish
Sashimi
Seafood Soup
Sushi
Udon
On the ground:
1 fish
1 honey
Even though fish soup, berry cake, udon etc needs much more than just the one fish that's in the area.
Can anyone help me figure this out? I'd be forever grateful; I've spent a few days already trying to myself. Please?
And before anyone says anything, I know I need to start using mysqli; unfortunately I didn't even realize it existed when I started to make the game (and learn PHP at the same time months ago), so I'll have to painfully go back and change it all in an update.
You want a HAVING clause to check the count of the records you are grouping through the INNER JOINs.
HAVING count(*) = (
SELECT count(*)
FROM req_crafting_edible
WHERE req_crafting_edible.created_item_id = items.item_id
)
Edit:
So basically you need to know two pieces of information:
How many different resources are required
Do each of those resources have the required amounts
The first is solved by the sub query above.
Your query as-is satisfies the second point but only for 1 resource.
HAVING basically does some special magic on your group clause. HAVING count(*) means there are X records being grouped together. Because of how the join works, you will have 1 item.name for each resource. The sub select gives you the count of how many different resources, and therefore grouped records, are needed for that item. Comparing that sub query with the count(*) of the grouping ensures you have all the needed resources.
And here is the final query, modifying your code above:
SELECT items.item_name, items.item_id
FROM items
INNER JOIN req_crafting_edible
ON req_crafting_edible.created_item_id = items.item_id
INNER JOIN edible_ground
ON edible_ground.resource = req_crafting_edible.edible_resource_id
AND edible_ground.amount >= req_crafting_edible.req_resource_amount
WHERE edible_ground.location = $current_location
GROUP BY items.item_name
HAVING count(*) = (
SELECT count(*)
FROM req_crafting_edible
WHERE req_crafting_edible.created_item_id = items.item_id
)
ORDER BY items.item_name
You only actually want the data from the items table, right? If so I would move to using an exists model:
SELECT I.item_name, I.item_id FROM items I
WHERE NOT EXISTS
(SELECT created_item_id
FROM req_crafting_edible R
WHERE R.created_item_id = I.item_id
AND NOT EXISTS
(SELECT G.resource
FROM edible_ground G
WHERE G.resource = R.edible_resource_id
AND edible_ground.location = $current_location
AND G.amount >= R.req_resource_amount))
ORDER BY I.item_name
I don't have your database to check this, but the logic goes like this:
Find the items that don't have any unsatisfied requirements.
Find the unsatisfied requirements for the current item. (IE. Find
requirements that don't have resources on the ground)
Find the edible resources that match the current requirement, are at
this location, and have enough.
I don't work in mysql as much at the moment, but let me know if this doesn't work.

Categories