List suggested friends - php

I'm trying to get a list of suggested friends that are not within my friends but are in my friends, friends.
status of 2 being an accepted friend.
My session id is 34 and I'm friends with user 3 and user 3 is friends with user 16, so user 16 would show as a suggested friend as user 16 is not my friend.
Friends table
id | user 1_id | user2_id | status
----------------------------------
4 3 34 2
3 34 3 2
2 3 16 2
1 16 3 2
Query (What I've tried)
$user1_id=$_SESSION['id'];
$user2_id=$data['id'];
$collectmutualfriendsa = mysqli_query($mysqli,"
SELECT DISTINCT r2.user1_id
FROM
friends r
INNER JOIN friends r2
ON r.user1_id = r2.user2_id
LEFT OUTER JOIN friends r3 ON r3.user2_id = r2.user1_id AND r3.user1_id=2
WHERE r.user2_id = 2 AND r3.user1_id is null");

$user1_id = $_SESSION['id']; // 34
$user2_id = $data['id']; // 3
$query = "SELECT * FROM Friends_table WHERE user1_id = '$user2_id' AND user2_id != $user1_id;";

Okay, I tried a UNION and now have it working.
$collectmutualfriendsa = mysqli_query($mysqli,"
SELECT *
FROM friends
WHERE user2_id NOT IN
(
SELECT user1_id FROM friends WHERE user2_id = '$user1_id'
UNION
SELECT user2_id FROM friends WHERE user2_id = '$user1_id'
)
");

Related

Selecting info in MySQL

i want to select all where my account exits
but am getting this error
DBD::mysql::st execute failed: Operand should contain 1 column(s)
USER
ID ACCOUNT
1 JOHN
2 JANE
3 JET
4 KAT
5 YMT
FRIENDS
ID ACCOUNT FRIEND
1 JOHN JET
2 JET JANA
3 KAT JOHN
NOTIC
ID ACCOUNT
1 JOHN
SELECT count(*) FROM notic WHERE account IN (SELECT account, friend FROM friends WHERE account = 'JOHN' OR friend = 'JOHN'
You can join the friends table on notic.account = friend.account or notic.account = friend.friend
Something like this
select count(*) from notic n join friends f on n.account = f.account || n.account = f.friend where n.account = 'JOHN';
Here is a fiddle showing it working

join inbox and sent box table

I am creating message conversation script in PHP mysqli. I have two table inbox and sent box, this two table same columns I want to join this two table.and i want to get last message between two users.
inbox table
id from_id to_id msg sent_date
1 2 3 hi how are you? 2019-12-05 04:14:20
2 3 2 fine 2019-12-05 05:15:58
3 2 3 hi 2019-12-05 03:20:34
4 5 2 hi 2019-12-05 08:30:40
sentbox table
id from_id to_id msg sent_date
1 2 3 hi how are you? 2019-12-05 04:14:20
2 3 2 fine 2019-12-05 05:15:58
3 2 3 hi 2019-12-05 03:20:34
4 5 2 hi 2019-12-05 08:30:40
Here is my source code
<?php
if (isset($_SESSION['userid'])) {
$session_id = $_SESSION['userid'];
}
$sql = "SELECT *,
(SELECT username FROM users WHERE userid=from_id) AS from_username,
(SELECT username FROM users WHERE userid=to_id) AS to_username,
(SELECT username FROM users WHERE userid=?) AS my_username,
(SELECT profile_pic FROM users WHERE userid=from_id) AS from_profile_pic,
(SELECT profile_pic FROM users WHERE userid=to_id) AS to_profile_pic,
(SELECT profile_pic FROM users WHERE userid=?) AS my_profile_pic
FROM inbox WHERE from_id = ? OR to_id = ? ORDER BY id DESC";
if ($stmt = $con->prepare($sql)) {
$stmt->bind_param('iiii', $session_id, $session_id, $session_id, $session_id);
$stmt->execute();
}
What about this?
SELECT * from inbox i
inner join sentbox s on i.from_id = s.to_id
inner join users u on u.user_id = i.from_id
where i.from_id = 'your desired id here'
order by i.sent_date DESC limit 1;
I believe this would give you the latest communication between 2 users.
Having said that, it feels like it would be better to go with a simpler design where every communication is a transaction and you store it in a single table, with FROM and TO fields and the time of the communication. No need to do a JOIN between 2 tables here. You're duplicating data here.

fetching details of user having same reporting email address using join or alias

I have two tables
tbl_user:
id fisrtname lastname
5 John Doe
6 Peter Parker
7 Will Smith
tbl_experience:
exp_id usr_id user_reporting_to
1 5 dev#abc.com
2 6 admin#abc.com
3 7 dev#abc.com
I want to fetch those record who have same reporting email address with where condition usr_id=5 because when i am login in to my page it creates the session of user id for e.g it is now 5 in where condition using join or alias
SELECT texperience.tbl_experience_report_to_email AS tbl_experience_report_to_email,
tuser.tbl_user_fname AS tbl_user_fname,
texperience.tbl_experience_designation AS tbl_experience_designation
FROM tbl_experience AS texperience,
tbl_user AS tuser
WHERE tuser.tbl_user_fname = tuser.tbl_experience_id
AND texperience.tbl_experience_report_to_email = texperience.tbl_experience_id
AND texperience.tbl_experience_user_id = 1
SELECT tbl_user.*, tbl_experience.user_reporting_to FROM tbl_user
LEFT JOIN tbl_experience ON (tbl_user.id=tbl_experience.usr_id)
LEFT JOIN (SELECT count(*) as total_user, user_reporting_to FROM tbl_experience GROUP BY user_reporting_to) as email_group ON
(email_group.user_reporting_to = tbl_experience.user_reporting_to) WHERE email_group.total_user > 1
Basically have a subquery that group all the email address and the joined table will return those users that have emails appearing more than once in the tbl_experience.

PHP MySQL Highscore table

Im joining 3 tables to present a table with users highest score
My tables
game_log:
---ID---user_ID---score---time---
| 1 52 567 10 |
| 2 53 641 13 |
| 3 52 465 8 |
| 4 53 451 14 |
---------------------------------
users:
---ID---name---countyid---
| 52 Dave 1 |
| 53 John 2 |
------------------------
county:
---countyid---countyname---
| 1 Orange wichit |
| 2 Orange clemts |
--------------------------
SQL:
SELECT * FROM game_log
INNER JOIN users ON game_log.user_ID=users.ID
INNER JOIN county ON users.countyid=county.countyid
ORDER BY game_log.score DESC , game_log.time LIMIT 20";
Above code gives me this result:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
3 John Orange clemts 465 8
4 Dave Orange wichit 451 14
My problem is that I want the highscore table to display the top 20 users with the highest score, not the 20 highest scores.
Like this:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
Need som help with this, not familiar with joining tables ;-)
This approach will show the top 20 users and each user's highest score, and if they have multiple instances of the same score, it'll show the information for the earliest one (lowest time value for that user and score).
SELECT *
FROM game_log gl
INNER JOIN users u
ON gl.user_ID = u.ID
INNER JOIN county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.score > gl.score)
and not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.time < gl.time
and gl2.score = gl.score)
ORDER BY gl.score DESC, gl.time LIMIT 20;
Without doing this, if the same user in the top 20 had the same score 2+ times, they would be listed 2+ times, and you would not get back 20 people by using LIMIT 20 because the same person would be taking up N rows out of that 20.
SQL Fiddle here showing data with a tie: http://sqlfiddle.com/#!2/0ac931/5/0
GROUP BY should do the job.
SELECT users.ID, users.name, county.countyname, MAX(game_log.score) AS score, game_log.time
FROM game_log
INNER JOIN users ON game_log.user_ID = users.ID
INNER JOIN county ON users.countyid = county.countyid
GROUP BY game_log.user_ID
ORDER BY game_log.score DESC, game_log.time
LIMIT 20;
Try it out with SQL Fiddle.
I would do this with the not exists approach to get the highest score for each user. The rest of the query is the same:
SELECT *
FROM game_log gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id and gl2.score > gl.score
)
ORDER BY gl.score DESC, gl.time
LIMIT 20;
The where clause is saying "keep this row if no other row for the same user has a higher score".
Another way to do this is with the aggregation approach:
SELECT *
FROM (select user_id, max(score) as maxscore
from game_log gl
group by user_id
) gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
ORDER BY gl.maxscore DESC
LIMIT 20;
But this method loses the information about time. It is possible to include that, but it makes the query more complicated.

How to limit / offset query that returns several rows per unique ID?

My issue is that I need to paginate data from this query:
function search($search_term, $limit, $offset)
{
$id = $this->auth->get_user_id();
$query = $this->db->query("
SELECT user_id,
first_name,
cars_name,
cars_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY first_name ASC
");
$search_data = array();
foreach ($query->result() as $row) {
$search_data[$row->user_id]['name'] = $row->first_name;
$search_data[$row->user_id]['cars'][$row->cars_id] = array(
'cars_name' => $row->cars_name);
}
return $search_data;
}
A sample data table / query response would be:
1 JOE HONDA 123
1 JOE TOYOTA 124
2 MAC VW 125
2 MAC HONDA 126
2 MAC TESLA 127
3 STU SUBARU 128
3 STU KIA 129
-----------
Page 1
-----------
1 JOE HONDA 123
TOYOTA 124
2 MAC VW 125
HONDA 126
------------
Page 2
------------
3 STU SUBARU 128
KIA 129
If I enter a limit and offset at the end of MySQL query
...
LIMIT $limit
OFFSET $offset;
");
the limit and offset are applied to the total number of rows, not the the number of rows grouped by user.
I've tried using GROUP BY but was unable to make it work.
My goal is to make the query as above but LIMIT and OFFSET the query by a number of rows that counts users, not all rows.
Any ideas?
I don't see a way to do this in one query. My solution would be to get the count of unique ID's using a group by query with the same parameters:
SELECT COUNT(1) AS uid_count
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
GROUP BY user_profiles.user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
Then fetch the uid_countmysql_num_rows and use that to calculate pagination variables for the above query.
The solution really is to use a GROUP BY clause:
SELECT SQL_CALC_FOUND_ROWS
user_id,
first_name,
cars_name,
cars_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
GROUP BY user_id
ORDER BY first_name ASC
LIMIT 100
The order is important. GROUP BY first, then ORDER BY, and then OFFSET/LIMIT.
Notice the SQL_CALC_FOUND_ROWS up there? After the query has executed, if you want to get the total row count (including those who aren't returned because of the LIMIT clause), just use:
SELECT FOUND_ROWS() AS `count`
And fetch the count column.
However, like you said, the rows will collapse and you will lose some cars_name and cars_id values.
Another solution is to use GROUP_CONCAT, then split it in PHP:
SELECT
user_id,
first_name,
GROUP_CONCAT(cars_name SEPARATOR ','),
GROUP_CONCAT(cars_id SEPARATOR ','),
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY first_name ASC
LIMIT 100
This would give you something like:
1 JOE HONDA,TOYOTA 123,124
2 MAC VW,HONDA,TESLA 125,126,127
3 STU SUBARU,KIA 128,129
If you want to get a list like this
Page 1
----------------------
1 JOE HONDA 123
1 JOE TOYOTA 124
Page 2
----------------------
2 MAC VW 125
2 MAC HONDA 126
2 MAC TESLA 127
Page 3
----------------------
3 STU SUBARU 128
3 STU KIA 129
Forget about limit, do this instead:
A - First retrieve a list of user id's and insert that into a temp table
CREATE TEMPORARY TABLE `test`.`temp_user_ids` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INTEGER UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE = MEMORY
B - Next insert the relavant user_id's into the table.
INSERT INTO temp_user_ids
SELECT null, user_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY user_id DESC /*insert in reverse order !*/
The lowest user_id is the last_insert_id in the temptable, and the temp_table
items are in sequential order.
C - Set the SQL #var #current_id to the last_insert_id in the temp_table.
SELECT #current_id:= LAST_INSERT_ID()
D - Next select relevant rows from the table, using only the user_id you want.
SELECT count(*) as row_count,
up.user_id,
first_name,
group_concat(cars_name) as car_names,
group_concat(cars_id) as car_ids,
FROM user_profiles up
LEFT JOIN cars
ON cars.id_fk = up.user_id
INNER JOIN temp_user_ids t
ON (t.user_id = up.user_id)
WHERE t.id = #current_id
GROUP BY up.user_id
ORDER BY cars.id
E - Now lower the #current_id
SELECT #current_id:= #current_id - 1;
F - And repeat step D and E until there's no more rows to be had.
The first field row_count tells you the number of rows aggregated in the fields
car_names and car_ids. You can separate these fields by using php's explode.

Categories