How to combine sql select queries for this situation? - mysql

i'm writing a chat app with php/mysql
i have 3 tables: user, room and room_participant with these structures:
user: id, username
room: id, title
room_participant: room_id, user_id
Now i want to get list of all rooms along with list of all participants in each room.
Until now i just select all rooms from room table and iterate through all rooms and select users information out of each entry, which is very inefficient.
Is there any way to combine all these select into only 1 select query?

Not certain about this without testing, but give it a try:
SELECT
room.*,
user.*
FROM room
JOIN room_participant ON room_id = room_participant.id
JOIN user ON room_participant.user_id = user.id
ORDER BY room.id
To deduplicate rooms, use GROUP_CONCAT()
UPDATE GROUP_CONCAT() modified to return id|username
SELECT
room.id, room.name
GROUP_CONCAT(CONCAT(user.id,'|',user.username)) AS userlist
FROM room
JOIN room_participant ON room_id = room_participant.id
JOIN user ON room_participant.user_id = user.id
GROUP BY room.id, room.name
ORDER BY room.id
With the userlist generated by GROUP_CONCAT as id|name,id|name,id|name you can use PHP explode() to separate them.
// Split the list on the commas
$users = explode(",", $userlist);
$final_users = array();
// Then split each on the `|`
foreach ($users as $user) {
$split_user = explode("|", $user);
// Append each as a new associative array to $final_users
$final_users[] = array('id' => $split_user[0], 'username' => $split_user[1]);
}
// Now you have an array of users as id, username
var_dump($final_users);

You may wish to do two queries, and then match things up in whatever is using MySQL, e.g. PHP.
These two:
SELECT id, title
FROM room;
SELECT rp.room_id, rp.user_id, u.username
FROM room_participant AS rp
INNER JOIN user as u ON rp.user_id = u.id;
Or these two:
SELECT id, username
FROM user;
SELECT rp.room_id, rp.user_id, r.title
FROM room_participant AS rp
INNER JOIN room as r ON rp.user_id = r.id;
Which two queries make sense depends on what you're doing with the info really.
You could go a step further and select all three separately:
SELECT *
FROM room;
SELECT *
FROM user;
SELECT *
FROM room_participant;
Note: It's probably better to state the columns, rather than using '*', just in case in the future new columns are added to the table that you're not really interested in for these queries.
Obviously, you'd then have to match everything up in whatever is using MySQL, e.g. PHP. You could create a list of rooms and users from the selected info, and then match them up with something like:
// Use MySQL to populate $roomList from database, then do...
foreach ($roomList as $room)
{
$id = $room['id'];
$title = $room['title'];
$this->roomList[$id] = new Room($id, $title);
}
// Use MySQL to populate $userList from database, then do...
foreach ($userList as $user)
{
$id = $user['id'];
$username = $user['username'];
$this->userList[$id] = new User($id, $username);
}
// Use MySQL to populate $roomParticipantList from database, then do...
foreach ($roomParticipantList as $roomParticipant)
{
$room = $this->roomList[$roomParticipant['room_id']];
$user = $this->userList[$roomParticipant['user_id']];
// You could do one/both of these, depending on requirements.
$room->enterUser($user);
$user->joinRoom($room);
}

Related

PHP/PDO Select from columns multiple conditions

I am trying to obtain results for a given member where status is pending or accepted doing the below:
$status1 = "Pending";
$status2 = "Attended";
$query = $conn->prepare('SELECT * FROM members WHERE member_id=:mID AND status=:status1 OR status=:status2');
$query->execute(array(':mID' => $mID,':status1' => $status1, ':status2' => $status2));
if ($query->rowCount() > 0) {
//start to create my table
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
//create variable, loop through and fill the table etc
}
}else{
echo "something";
}
This displays data - however, it even obtains results not specific to the member id (mID). Meaning other members data too!
I'm clearly missing something and or my query is wrong but struggling to find anything..
Any help appreciated.
You need to look at operator precedence for your database. You're doing this:
SELECT * FROM members WHERE member_id = :mID AND status = :status1 OR status = :status2;
Which most likely results in this:
SELECT * FROM members WHERE (member_id = :mID AND status = :status1) OR status = :status2;
But that's not what you want, so you will have to explicitly use parens like this:
SELECT * FROM members WHERE member_id = :mID AND (status = :status1 OR status = :status2);
Alternatively, you can use IN so that there's no OR:
SELECT * FROM members WHERE member_id = :mID AND status IN (:status1, :status2);

Query for retrieving list of items based on two different lists

Problem: Searching employees based on document numbers.
Input: List of document nos. and document types
Expected result: list of employees for corresponding document nos AND types.
tables I have:
Table 1:employee
empID - person
Table 2:document
docNo - docType - id
Please note: employee.person.id = doc.parent.id - this is true by database design
Wrote the query like:
SELECT employee
from Employee employee, Document doc
WHERE doc.docType IN :docTypeList
and doc.docNo IN :docNoList
and employee.person.id = doc.parent.id
I know it wont work, because of "doc.docType IN :docTypeList and doc.docNo IN :docNoList", but couldn't think alternative. Any leads to modify the query so that I can handle both docTypeList and docNoList to find employees that matches both of them.
Looks like you just need to join the tables on the ID value and add the right values in your select clause:
SELECT a.employee, b.docType, b.docNo
FROM employee a JOIN document b
ON a.empID=b.id
here is an example using the naming conventions(sort of) you have in your original question:
SELECT emp.employee, doc.docType, doc.docNo
FROM Employee emp JOIN document doc
ON emp.empID=doc.id
Unrelated to your question but when using tablename aliases you should probably shorten the alias otherwise you can just type out the name of the table everytime. The point of an alias is to make your code cleaner and easier to type by using shortened tablenames. See above - I changed employee alias to "emp"
I'm more SQL server so presuming your list parameters are like tableTypes....
SELECT employee
from Employee employee
INNER JOIN Document doc ON doc.parent.id = employee.person.id
WHERE
doc.docType IN ( Select doctype FROM :docTypeList )
and doc.docNo IN (Select doc no FROM :docNoList )
When you need to pass a list of parameters to IN(), the solution is to break down your list and pass each item as a separate parameter to the SQL statement.
Assuming MySQL + PDO:
$paramCount = 0;
$params = []; // holds parameter values
$typesParamNames = $numbersParamNames = []; // parameter names for each list
// document types
foreach ($docTypeList as $val)
{
++$paramCount;
$paramName = ":p{$paramCount}";
$params[$paramName] = $val;
$typesParamNames[] = $paramName;
}
// document numbers
foreach ($docNoList as $val)
{
++$paramCount;
$paramName = ":p{$paramCount}";
$params[$paramName] = $val;
$numbersParamNames[] = $paramName;
}
// build the SQL
$typesSqlFragment = implode(',', $typesParamNames);
$numbersSqlFragment = implode(',', $numbersParamNames);
$sql = "SELECT employee from Employee employee, Document doc WHERE doc.docType IN ($typesSqlFragment) and doc.docNo IN ($numbersSqlFragment) and employee.person.id = doc.parent.id";
$stmt = $dbh->prepare($sql);
// pass the collected parameters values to the prepared statement
foreach ($params as $name => $val) {
$stmt->bindParam($name, $val);
}
// run it
$stmt->execute();

How to perform Inner joins , left joins with readbean ORM?

If I have 2 tables one is users and one is stores , the users id field is associated with the store's user_id field .
Now if I want to find all those users who has a store how can I perform it on readbean ?
and please do explain as I'm just getting started with it.
Thanks
If your queries looking complex, You can simply use plain sql inside redbean.
$records = R::getAll("SELECT * FROM tbl1 LEFT JOIN tbl2 ON tbl1.id = tbl2.tbl1_id");
This will result into and all satisfying records array.
Here I have used R::getAll($your_qry) method, to fetch for single row use R::getRow($yoyr_sql_qry); method.
If you have any difficulties. let me know.
Is it a 1:Many or Many:Many?
If I understand what you said, it's 1:Many
DB model: stores belong to users
So, a store can belong to exactly one (1) user, correct?
If so, it's easy using redbean
$user = R::dispense('users'); // create a user
$store = R::dispense('stores'); // create a store
$store2 = R::dispense('stores'); // create a store
$store1->name = 'Foo';
$store2->name = 'Bar';
$user->xownStoresList[] = $store; // save user ( and store )
$user->xownStoresList[] = $store2; // save user ( and store )
$id = R::store( $user );
foreach ( $user->ownStoresList as $store ) {
echo $store->name . ', ';
}
// outputs: "foo, bar,"

php codeigniter MySQL search query

I want to create a search query on MySQL database that will consist of 5 different strings typed in from user. I want to query 5 different table columns with these strings.
When I for example have input fields like:
first name, last name, address, post number, city.
How should I query the database that I dont always get all the rows.
My query is something like this:
SELECT user_id, username
from users
where
a like %?% AND
b like %?% AND
c like %?% AND
d like %?% AND
e like %?%;
When I exchange the AND for OR I always get all the results which makes sense, and when I use AND I get only the exact matches...
Is there any function or statement that would help me with this?
EDIT
The code I use is:
$sql = "select users.user_id, first_name
from users
inner join user_normal_aos
on users.user_id = user_normal_aos.user_id
inner join normal_areas_of_expertise
on user_normal_aos.normal_areas_of_expertise_id = normal_areas_of_expertise.normal_areas_of_expertise_id
where
users.first_name like ? AND
users.kanzlei like ? AND
normal_areas_of_expertise.normal_aoe like ? AND
users.postcode like ? AND
users.city like ?";
$query = $this->db->query($sql,
array(
'%'.$lawyer_name.'%',
'%'.$kanzlei.'%',
'%'.$area_of_expertise.'%',
'%'.$post_code.'%',
'%'.$city.'%')
);
For example use PHP to adjust your query based on what fields you have entered.
$where = array();
$replacements = array();
/* you can also compare if string is not null or not empty...
this is just example using isset */
if (isset($lawyer_name)) {
$where[] = 'users.first_name like ?';
$replacements[] = '%'.$lawyer_name.'%';
}
/* repeat this if again for all your fields .... */
$sql = "..... where ".implode(' AND ', $where);
$query = $this->db->query($sql,
$replacements
);

Writing a MYSQL Following Users Query

I'm building an app where a user can follow other users and be followed.
A user can also look at who another user is following.
Now lets say user1 is looking at who user2 is following, I need to find all the people IDs that user2 is following and compare it against who user1 is following.
Instead of returning only the IDs of all the users that match both user1 and user2 (which I've seen in other forums), I need to retrieve all user2's following IDs and User Names as well as a flag that indicates if the followed person is also followed by user1.
I've got it to work in PHP with a double for loop of each Query, but I worry that this code will be expensive and would be far better optimized with a single MYSQL query.
Relevant tables and columns:
following_table
follower_id
followed_id
following: varchar -- 'true' or 'false'
user_table
user_id
user_name
Here is my PHP code:
$user_id1 = '1991';
$myFollowingQuery = "SELECT following_table.followed_id, user_table.user_name
FROM following_table
INNER JOIN user_table ON
following_table.followed_id = user_table.user_id
WHERE following_table.following = 'true'
AND following_table.follower_id = '$user_id1'";
$user_id2 = '1985';
$userFollowingQuery = "SELECT following_table.followed_id, user_table.user_name
FROM following_table
INNER JOIN user_table ON
following_table.followed_id = user_table.user_id
WHERE following_table.following = 'true'
AND following_table.follower_id = '$user_id2'";
$userFollowingResult = mysql_query($userFollowingQuery)
or doResponse('error',"Couldn't connect to the database");
$myFollowingResult = mysql_query($myFollowingQuery)
or doResponse('error',"Couldn't connect to the database");
for($i = 0; $i< mysql_num_rows($userFollowingResult);$i++){
$loopArray = array(followed_id => mysql_result($userFollowingResult,$i,"followed_id"),
followed_name => mysql_result($userFollowingResult,$i,"user_name"));
for($j = 0; $j< mysql_num_rows($myFollowingResult);$j++){
if(mysql_result($userFollowingResult,$i,"followed_id")
==mysql_result($myFollowingResult,$j,"followed_id")) {
$loopArray['is_following'] = 'true';
break;
}
if($j==mysql_num_rows($myFollowingResult)-1){
$loopArray['is_following'] = 'false';
break;
}
}
$resultArray[$i] = $loopArray;
}
echo json_encode($resultArray);
Here is a simplified query:
http://sqlfiddle.com/#!2/6b8d6/3
SELECT
user.user_id,
user.user_name,
he.follower_id AS follower_id,
IF(me.followed_id,1,0) AS metoo
FROM following AS he
INNER JOIN user
ON user.user_id = he.followed_id
LEFT JOIN following AS me
ON me.follower_id = 1
AND me.followed_id = he.followed_id
WHERE he.follower_id = 2