MySQL pulling information from multiple table issue - php

I'm trying to access information from 2 different tables and its retrieving the information but it all so pulling in user information multiple times pending how many fruits listed in table tbl_fruits.
like to be able to display user information once and pull what ever number of fruits associated with the user at the same time.
2 tables:
tbl_users:
userid
firstname
lastname
tbl_fruits:
userid
fruit
in the example code userID 4 has 3 fruits associated with him in tbl_fruit. As you can see from the results below the user information is listed multiple times. How can I rewrite the code so that the user information is pulled once and the fruits show up 3 times.
$clientID = "4";
try
{ $stmt = $dbcon1 ->query("SELECT
tbl_fruits.fruit,
tbl_users.userid,
tbl_users.firstname,
tbl_users.lastname
FROM tbl_users
LEFT JOIN tbl_fruits
ON tbl_fruits.userid = tbl_users.userid
WHERE tbl_users.userid = '$clientID' ");
$testArray = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
catch(PDOException $e)
{ echo $e->getMessage(); }
echo '<pre>';
print_r($testArray);
echo '</pre>';
results
array
(
[0] => Array
(
[fruit] => Apple
[userid] => 4
[firstname] => John
[lastname] => Smith
)
[1] => Array
(
[fruit] => Orange
[userid] => 4
[firstname] => John
[lastname] => Smith
)
[2] => Array
(
[fruit] => Banana
[userid] => 4
[firstname] => John
[lastname] => Smith
)
)

Change your query as below:
{ $stmt = $dbcon1 ->query("SELECT
count(tbl_fruits.userid)
tbl_users.userid,
tbl_users.firstname,
tbl_users.lastname
FROM tbl_users
LEFT JOIN tbl_fruits
ON tbl_fruits.userid = tbl_users.userid
WHERE tbl_users.userid = '$clientID'
GROUP BY tbl_users.userid ");
The count(tbl_fruits.userid) part counts the number of unique rows with that row's value of tbl_fruits.userid, while the GROUP BY tbl_users.userid part eliminates the duplicates.
Be sure to put an index on tbl_users.userid and tbl_fruits.userid for best performance.

Related

How to create a three dimensional array from sql query PHP

I have 2 tables in a database, 1 of them are linked with a foreign key to the first one. Each row on table 1 is linked to multiple rows in table 2. I am trying to make a query that looks at a WHERE from table 2 and returns multiple rows from table 2 which are sorted into the rows they linked with in table 1 and then put this all into one big multi dimensional array, so it should work something like this:
$array[0][column_name][0] this would use row 1 from table 1 and give me a the first result in the column called column_name
$array[1][column_name][0] this would use row 2 from table 1 and give me a the first result in the column called column_name
$array[1][column_name][3] this would use row 2 from table 1 and give me a the 4th result in the column called column_name
etc
How can I query this and store it in a 3 dimensional array using PHP.
I have tried to word this in as clear manner as possible, if you are unsure what I am asking, please comment and I will update my question to make it clearer.
Assume that we have two tables, Company and Employee:
Company
------------------
ID Company_Name
1 Walmart
2 Amazon.com
3 Apple
Employee
---------------------------------
ID Company_Id Employee_Name
1 1 Sam Walton
2 1 Rob Walton
3 1 Jim Walton
4 1 Alice Walton
5 2 Jeff Bezos
6 2 Brian T. Olsavsky
7 3 Steve Jobs
8 3 Tim Cook
The easiest way to envision a multi-dimensional (nested) array is to mimic the looping required to get it: outer loop is the company, inner loop is the employees:
// ignoring database access, this is just pseudo code
$outer = [];
// select id, company_name from company
foreach $companyResult as $companyRow {
// select * from employee where company_id = ? {$companyRow['id']}
$inner= [];
foreach $employee_result as $employeeRow {
$inner[] = $employeeRow; // ie, ['id'=>'1','Company_Id'=>'1','Employee_Name'=>'Sam Walton']
}
$outer[] = $inner;
}
print_r($outer);
// yields ====>
Array
(
[0] => Array
(
[0] => Array
(
[id] => 1
[Company_Id] => 1
[Employee_Name] => Sam Walton
)
[1] => Array
(
[id] => 2
[Company_Id] => 1
[Employee_Name] => Rob Walton
)
[2] => Array
(
[id] => 3
[Company_Id] => 1
[Employee_Name] => Jim Walton
)
[3] => Array
(
[id] => 4
[Company_Id] => 1
[Employee_Name] => Alice Walton
)
)
[1] => Array
(
[0] => Array
(
[id] => 5
[Company_Id] => 2
[Employee_Name] => Jeff Bezos
)
[1] => Array
(
[id] => 6
[Company_Id] => 2
[Employee_Name] => Brian T. Olsavsky
)
)
[2] => Array
(
[0] => Array
(
[id] => 7
[Company_Id] => 3
[Employee_Name] => Steve Jobs
)
[1] => Array
(
[id] => 8
[Company_Id] => 3
[Employee_Name] => Tim Cook
)
)
)
It is also possible to do if you use associative arrays. Consider the flat file that this query produces:
select company.id company_id, company.name company_name,
emp.id employee_id, emp.employee_name
from company
inner join employee on company.id = employee.company_id
-----
company_id company_name employee_id employee_name
1 Walmart 1 Sam Walton
1 Walmart 2 Rob Walton
1 Walmart 3 Jim Walton
1 Walmart 4 Alice Walton
2 Amazon.com 5 Jeff Bezos
2 Amazon.com 6 Brian T. Olsavsky
3 Apple 7 Steve Jobs
3 Apple 8 Tim Cook
Just use the primary IDs as the keys for your arrays:
$employeeList = [];
foreach($result as $row) {
$cid = $row['company_name'];
$eid = $row['employee_name'];
// avoid uninitialized variable
// $employeeList[$row['company_name']] = $employeeList[$row['company_name']] ?? [];
// easier to read version of above
$employeeList[$cid] = $employeeList[$cid] ?? [];
// assign it...
$employeeList[$cid][$eid] = $row;
}
Or, if you simply want each company row to hold an array of employee names,
$employeeList[$cid][] = $row['employee_name'];
The way that I've shown you is useful if you know the company_id and want to find the associated rows:
foreach($employeeList[2] as $amazon_guys) { ... }
But it's not at all useful if you're trying to group by employee, or some other field in the employee table. You'd have to organize the order of your indexes by your desired search order.
In the end, it's almost always better to simply do another query and let the database give you the specific results you want.

Querying a database with a one to many relationship

I am making a database that when users register, they pick 3 games they would like to play. the games are stored in a separate table (gameinfo) from the user information table (personalinformation). I am querying with the first game being shown but I would like all three shown for each user. How would I implement showing all the games?
I have tried to create different variables for each game, but that has seemed to not work as I expected and broke. the games when they are stored on the personalinformation table are stored as numbers like 1 or 2. these are linked to the gameinfo table and are the primary key for each game.
Structure of database
https://imgur.com/a/qee9C1t
$conn = mysqli_connect('localhost', 'root', '', 'esportclub');
$sql = "SELECT user_ID, username, Email, Gender, firstName, lastName, gameName FROM personalinformation, gameinfo WHERE game_id = firstGame";
$result = mysqli_query ($conn, $sql);
if (mysqli_num_rows($result) > 0) {
echo "<table>";
while($row = mysqli_fetch_assoc($result)) {
echo " <tr><td> Name: ". $row{"username"}. " </td><td> Email: ". $row{"Email"}. " </td><td> Gender: ". $row{"Gender"}. "</td>" .
"<td> First Name: ". $row{"firstName"}. " </td><td> First Game: ". $row{"gameName"}. "</td><td> Last Name: ". $row{"lastName"}. "</td>" . "</td></tr>" ;
}
echo "</table>";
}
else{
echo "0 results";
}
$conn->close();
As mentioned in my comment, I would create a table to associate users and games by storing unique pairs of user_ID and game_id values. Then I'd JOIN the tables together accordingly.
However, I see that you are storing three game values for each user in the personalinformation table, in columns named firstGame,secondGame, and thirdGame.
In that case, you can JOIN the game table to each of those columns.
So, with your existing structure:
SELECT
p.*,
game1.`gameName` as `firstGame_name`,
game2.`gameName` as `secondGame_name`,
game3.`gameName` as `thirdGame_name`
FROM `personalinformation` p
LEFT JOIN `games` as game1 ON (game1.`game_id` = p.`firstGame`)
LEFT JOIN `games` as game2 ON (game2.`game_id` = p.`secondGame`)
LEFT JOIN `games` as game3 ON (game3.`game_id` = p.`thirdGame`)
WHERE 1; // or WHERE p.`user_ID` = :user_ID;
EDIT
Since many users can own a game and a user can own many games, it sounds like a "many-to-many" relationship.
Here is my preferred method for that type of relationship. One advantage is that you don't need to limit the number of assigned games. That is, a user can own any number of games.
Create a third table to store unique user/game pairs.
It will tells you which games are assigned to which users.
Something like:
CREATE TABLE `user_game` (
`user_id` MEDIUMINT NOT NULL ,
`game_id` MEDIUMINT NOT NULL
);
ALTER TABLE `user_game`
ADD UNIQUE `unique pair` (`user_id`, `game_id`);
Then join the three tables together:
SELECT
u.*,
g.`game_id`,
g.`gameName`
FROM `personalinformation` u
LEFT JOIN `user_game` as ug ON ( ug.`user_id` = u.`user_ID` )
LEFT JOIN `games` as g ON ( g.`game_id` = ug.`game_id` )
WHERE 1;
You'll get back one row for every user/game relationship.
If one user has three games, that user will have three rows in the result, each row including one gameName.
For example:
Name Game
---- -----------------
Jane League of Legends
Jane Minecraft
Fred Dota 2
Alex Minecraft
Alex War Dragons
Alex Fortnite
More complex display might require some processing:
<?php
$users = array();
while($row= mysqli_fetch_object($result)) {
$uid = $row->user_ID;
// if this user isn't in the array...
if (!array_key_exists($uid,$users)) {
// ... create a user entry ...
$user = new stdClass();
$user->firstname = $row->firstName;
// ... and add it to the user array.
$users[$uid] = $user;
}
// if this row has a valid game ...
if (!empty($row->game_id)) {
// ... create a game entry ...
$game = new stdClass();
$game->id = $row->game_id;
$game->name = $row->gameName;
//.. and add the game to the user's entry
$users[$uid]->games[$game->id]=$game;
}
}
For a structure like this:
Array
(
[1] => stdClass Object
(
[firstname] => Jane
[games] => Array
(
[1] => stdClass Object
(
[id] => 1
[name] => Leage of Legends
)
[2] => stdClass Object
(
[id] => 2
[name] => Minecraft
)
)
)
[2] => stdClass Object
(
[firstname] => Fred
[games] => Array
(
[3] => stdClass Object
(
[id] => 3
[name] => Dota 2
)
)
)
[3] => stdClass Object
(
[firstname] => Alex
[games] => Array
(
[2] => stdClass Object
(
[id] => 2
[name] => Minecraft
)
[4] => stdClass Object
(
[id] => 4
[name] => War Dragons
)
[5] => stdClass Object
(
[id] => 5
[name] => Fortnite
)
)
)
)

How to use find in set for user rankings based on score?

I have query like this,
SELECT * FROM users ORDER BY score
So, the result is like this.
Array
(
[0] => stdClass Object
(
[userid] => 3
[user] => John Doe
[score] => 50
)
[1] => stdClass Object
(
[userid] => 1
[user] => Mae Smith
[score] => 38
)
[2] => stdClass Object
(
[userid] => 2
[user] => Mark Sam
[score] => 26
)
)
But, I want to add a rank using find_in_set query. So the result might be like this. So that the user can view their ranks when they login to their account.
Array
(
[0] => stdClass Object
(
[userid] => 3
[user] => John Doe
[score] => 50
[rank] => 1
)
[1] => stdClass Object
(
[userid] => 1
[user] => Mae Smith
[score] => 38
[rank] => 2
)
[2] => stdClass Object
(
[userid] => 2
[user] => Mark Sam
[score] => 26
[rank] => 3
)
)
I tried this one.
$listOfUser = array();
foreach($users as $user) {
$listOfUser[] = $user->userid;
}
And used another query
$userid = 2 // => id of loggedin user
SELECT *, find_in_set($userid, $listOfUser) as rank FROM users where userid=$userid ORDER BY score
So, I got this result
Array
(
[1] => stdClass Object
(
[userid] => 2
[user] => Mark Sam
[score] => 26
[rank] => 3
)
)
Which is somehow correct. But, is there another way of querying that result using only one SQL query and without using foreach loop?
Something like this.
$userid = 2 // => id of loggedin user
SELECT *, find_in_set($userid, (SELECT * FROM users ORDER BY score)) as rank FROM users where userid=$userid ORDER BY score
But I got this error Subquery returns more than 1 row
If You don't insist on using find_in_set, you can get result with simple join. You ask for list of users (p) and for each user you ask, how many users have better score than him or her (c):
SELECT p.userid, COUNT(c.userid) AS rank
FROM users AS p
LEFT JOIN users AS c ON c.score > p.score
GROUP BY p.userid
This works even if you add other conditions, like WHERE p.userid = 123.
If more users have the same score, the ranks would look like 0,1,2,2,2,5,6.
In your query, you can add counter, like this:
set #n:=0;
SELECT #i := #i + 1 AS rank, * FROM users ORDER BY score
The rank here is relative to the score distribution across all users. I believe you should try something originally proposed in this answer:
SELECT users.*,
#rownum := #rownum + 1 as rank
FROM users
CROSS JOIN (select #rownum := 0) r
ORDER BY score DESC
What it does is basically order all users by score, and assign each of them an incremental value "rank". So the top scorer would have a rank of 1, the second scorer would have a rank of 2 etc.
Keep in mind that this solution is not "fair" - each user will have a different rank, even if all users have the same score. If you try to rank users as they do in sports (if two top competitors have the same score, they both take 1st place, and the next best competitor takes 3rd place, not second), you should think of a different solution.

MYSQL Innerjoin only show one record

I am busy with a ticket systeem and i have the question in a table, users information in an other table and the responses in an other table.
What i want is to get the Question (Table 1) with the user information by user ID and all the responses (also with user information by id)
Now i have the following code:
public function ticketSingle($id=""){
$sql = "
SELECT ticketSubmitted.*, users.Name, users.email, users.phone, ticketResponse.*
FROM ticketSubmitted
INNER JOIN users
ON users.id = ticketSubmittedUserId
JOIN ticketResponse
ON ticketId = ticketSubmittedID
WHERE ticketSubmittedID = '".$id."'
";
$result = $this->run($sql, $bind);
return $result[0];
}
Table 1 = ticketSubmitted (the question, with an userID "ticketSubmittedUserId")
Table 2 = users (The user information)
Table 3 = ticketResponse (The table with the reactions)
But if i Print the results i get only one record of the TicketResponse and what i want is all the reactions.
Can someone help me out?
This is what the function return:
Array
(
[ticketSubmittedID] => 1
[ticketSubmittedUserId] => 1
[ticketSubmittedDate] => 2018-02-05 16:00:00
[ticketSubmittedTitle] => Question Title
[ticketSubmittedMessage] => Hello World!
[ticketSubmittedUserIp] => XXX.XXX.XXX.XXX
[ticketSubmittedStatus] => 1
[Name] => John Doe
[email] => john#doe.com
[phone] => 0612345678
[ticketResponseId] => 1
[ticketId] => 1
[ticketUserId] => 2
[ticketMessage] => Hello Reaction
[ticketDate] => 2018-02-05 17:05
[ticketIp] => XXX.XXX.XXX.XXX
)
You are returning only the first row return $result[0]; 0 in the first index of the result. so you should return all eg:
return $result;
and loop over the result for manage what you need
foreach($result as $key => $value){
echo $value['ticketResponseId'];
}
You can check the real return content using
var_dump($result);

MySQL: fetch a row and multiple related rows - possible?

Let's say I have one table: "cars" with 3 fields: id, brand, cost.
There's a second table: "models" with 3 fields: id, brand, model_name.
Each "cars" row can have multiple related "models" rows.
Is it possible to do an sql-select whose output looks like this?
edit: I use PHP for the database querys
array(
[0] => array(
[id] => 1
[brand] => mercedes
[cost] => 1000
[models] => array(
[0] => array(
[id] => 1
[brand] => mercedes
[model_name] => slk
)
[1] => array(
[id] => 2
[brand] => mercedes
[model_name] => clk
)
[2] => array(
[id] => 3
[brand] => mercedes
[model_name] => whatever
)
)
)
)
You need to add a foreign key relation to the models table, say car_id. Then:
SELECT * FROM cars JOIN models ON car_id = models.id;
This will output something similar to what you are looking for.
Assuming you are using PHP, using the output:
$query= "SELECT * FROM cars JOIN models ON car_id = models.id";
$r= #mysqli_query($dbc, $query);
while ($row= mysqli_fetch_array($r, MYSQLI_ASSOC)) {
$carstuff['id']=$row[id];
$carstuff['brand']=$row[brand];
$carstuff['cost']=$row[cost];
$carstuff[$row['models']][]=$row['model_name'];
}
var_dump($carstuff);
Note, that the id, brand and cost are repeatedly overwritten, but that is okay because they are overwritten with the same information. I'm not too sure about the cleanliness of the code, but that is the basic idea.
Try this:
Query:
SELECT c.ID, c.brand,c.cost, GROUP_CONCAT(model_name SEPARATOR '","') as models
, GROUP_CONCAT(m.ID SEPARATOR ',') as MID
, GROUP_CONCAT(m.brand SEPARATOR '","') as mbrand
FROM cars c
LEFT OUTER JOIN model m
ON m.brand = c.brand
GROUP BY brand;
Output:
ID BRAND COST MODELS MID MBRAND
1 audi 1000 m11","m22 4,5 audi","audi
1 mercedes 1200 m1","m2","m3 1,2,3 mercedes","mercedes","mercedes
Now in your php code you can process the MODEL,MID and MBrand
(By using explode)
$modelArray = explode(" ,", $row["MODELS");
SQLFIDDLE

Categories