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;
Related
I have "reservation" table (mySql) that contain number of columns: res_id, hotel_id, hotel_name, from_date, to_date.
I would like to select and print html table for each hotel (i'm using PHP). the result should be a title - the name of the hotel, and bellow it a list of reservation for the specific hotel.
I can do GROUP BY:
Select * FROM reservation GROUP BY hotel_id
I'm not sure if it's the right way to do it, and how do i print the results without checking all the time if the hotel_id was changed?
Thank you in advanced
GROUP BY is definitely NOT the right way to approach this. One method would be:
SELECT *
FROM reservation
ORDER BY hotel_id;
You would then loop through the result sets. When the hotel name changes, you would put in the title of the hotel.
Note: This is a poor data model if it has both the hotel id and name in reservation. This would normally be in hotel and you would connect the tables using JOIN:
SELECT h.hotel_name, r.*
FROM hotels h JOIN
reservation r
ON r.hotel_id = h.hotel_id
ORDER BY hotel_id;
Using a LEFT JOIN, you can even get hotels with no reservations.
How is it that the hotel_id would change? As per your question it seems that hotel_id is a column made for join with a "hotels" table, isn't it?
Regarding the "group by", why would you group by hotel? This would make you loose reservations data, unless you were using some sort of group_concat.
If you want to get the reservations from a specific hotel you could loop through your hotels table and inside your loop you can do:
SELECT * FROM reservations WHERE hotel_id='QUERIED_HOTEL_ID'
Then show the results.
Or you could simply
SELECT * FROM reservations
And when you get the fetched results you can make a multidimensional php array with 'hotel_id' as top level key and 'res_id' as secondary, like this:
$reservations_by_hotel = [];
do {
$resId = $row['res_id'];
$hotelId = $row['hotel_id'];
$reservations_by_hotel[$hotelId][$resId] = $row;
} while ($row = $result->fetch_assoc());
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
Hi I am new for developing.Kindly bear my codings. I have created a table arlog with id(auto increment), status, ticket number and code. Ticket and code number is set as unique. That is the duplicate of this combination cannot inserted again. But individually ticket number or cpt can be inserted as many times.It works fine. Now I want to use select query with another table with respect to the arlog table ticket number and code.Here is the select statement
$result = mysql_query("SELECT * FROM `ar` C WHERE provider='".$_SESSION['PROVIDER']
."' AND C.`TicketNo` IN ( SELECT TicketNo FROM `arlog` L where L.status NOT IN('New','Completed','Completed(Ar_aging)
','Completed(Rework)','Rework','Completed_Followup','Completed_Supervising' )
and L.assign='".$_SESSION['NAME']."' ) order by id desc") or die(mysql_error());
The query check the ticket number in arlog and displays correcly. But I want to combine TicketNo and Code in the arlog. I have made research but could not find solution. First of all is it possible?
Please try following sql:
SELECT L.TicketNo ,L.Code,C.* FROM `ar` C left join `arlog` L ON C.TicketNo = L.TicketNo where C.provider='your condition' and L.status NOT IN('New','Completed','Completed(Ar_aging)','Completed(Rework)','Rework','Completed_Followup','Completed_Supervising' ) and L.assign='your condition' order by by C.id desc
Hope this can help you!
I think you need to use CONCAT_WS()
There is a nice example of its usage in below link
MySQL SELECT AS combine two columns into one
I am trying to follow the answer given in " Retrieving the last record in each group " but I have a problem
My problem is that I am echoing both Y78430's (ie count 1 and 3) when I only want to echo count 3
I am trying to pick the last record of groups of data where the last record is the lower alphabetical letter.
Example of my data here (Table is 'schedulelocation'):-
Count cif_train_uid cif_stp_indicator Other Data
1 Y78430 p zzzzzzz
2 Z45012 p fffffff
3 Y78430 o sssssss
In the above data there are 2 X Y78430. I would like to echo only one of these. The one with a cif_stp_indicator of o - ie its lower in the alphabet than the 'p'
Here is my code :-
$b="SELECT s1.cif_train_uid,s1.cif_stp_indicator,s1.schedule_start_date
FROM schedulelocation s1
LEFT JOIN schedulelocation s2
ON (s1.cif_train_uid AND s1.cif_stp_indicator < s2.cif_stp_indicator)
WHERE s2.cif_stp_indicator is Null AND s1.cif_train_uid='Y78430' ";
$l=mysqli_query($mysql_link,$b);
if ($l) {
while($berths=mysqli_fetch_array($l,MYSQLI_ASSOC))
{
echo $berths['cif_train_uid'];
echo $berths['cif_stp_indicator'];
echo $berths['schedule_start_date'];
echo "</b>";
echo "</b>";
}
}
Any help greatly appreciated. Thanks
You need to use an aggregate query to find the appropriate row to display, and join that to your basic information.
Your aggregate query is this:
SELECT cif_train_uid,
MIN(cif_stp_indicator) as cif_stp_indicator
FROM schedulelocation
GROUP BY cif_train_uid
It returns a list of pairs of (train, indicator) where the indicator is the lowest (lexically first) one for the train.
Then you INNER JOIN it into the rest of your query like this.
SELECT s1.cif_train_uid,s1.cif_stp_indicator,s1.schedule_start_date
FROM ( SELECT cif_train_uid, MIN(cif_stp_indicator) as cif_stp_indicator
FROM schedulelocation
GROUP BY cif_train_uid
) AS m
INNER JOIN schedulelocation s1
ON m.cif_train_uid=s1.cif_train_uid
AND m.cif_stp_indicator = s1.cif_stp_indicator
LEFT JOIN schedulelocation s2
ON s1.cif_train_uid
AND s1.cif_stp_indicator < s2.cif_stp_indicator)
WHERE s2.cif_stp_indicator is Null /* IS THIS CORRECT ?? */
AND s1.cif_train_uid='Y78430'
Note I'm not 100% sure what's going on with your self-join operation. I think your first WHERE clause eliminates all the rows where the self-join actually found something. I don't get it.
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.