mysql sub query to get latest comment from two tables - php

I have been working on a query to get summarised list of communications like an inbox that shows conversations
below are my tables
users
company | contact_person | pic_small
alerts
comment_id | user_id | poster_id | timestamp
activity
comment_id | user_id | comment | timestamp
comments
comment_id | user_id | comment | timestamp
This is what I have so far which works ok although I need help with one aspect.
SELECT alerts.comment_id,
alerts.user_id,
alerts.poster_id,
alerts.active,
MAX(alerts.timestamp) AS maxTime,
users.contact_person,
users.company,
users.pic_small
FROM alerts
LEFT JOIN users ON users.user_id = alerts.poster_id
WHERE alerts.user_id = %s
GROUP BY alerts.comment_id
ORDER BY maxTime DESC
Comments are held in the activity and comments tables and I need to somehow join the last (newest) comment from either the activity or comments table (depending which is newer)
How do I add this to my above query, below is what I am trying to achieve

Related

Like structure table with Laravel and showing the most popular content at top

I am using Laravel to creating a website, my users can post questions and other users can write their comments under the post, each comment have Up vote and Down vote, and users can voting for comments.
I need most liked (Up vote) shows topper than others..
This is my database structure and I join them together:
comment table:
comment_id | question_id | user_id | timestamp
up and down votes table (like):
like_id | comment_id | like_type | user_id | timestamp
note:like_type is an enum on mysql and its values are upvote and downvote.
1-What is the mysql query and Laravel codes for that?
2-Is my database Structure right?
1.Calculate SUM belongs to Each Comment
2.Make it order by Desc
select * from (
select ct.comment_id,ct.question_id,
ct.user_id,SUM(case when vt.like_type='upvote' then 1 else -1 end )
cnt from commenttable ct
from votestable
vt left join
on
vt.comment_id=ct.comment_id
)D order by D.cnt desc

MySql - Get latest rows from table without duplicate subject id

I am attempting to get the latest threads that have been posted on in a Forum with vars from Users, Threads and the last Post. Problem is that the current method i am attempting brings back duplicate threads because the newer posts have been posted in those threads, whereas i just want one post to return per thread, not all the latest posts.
SELECT t.thread_id, u.user_name, p.post_entry
FROM forum_thread as t
LEFT JOIN forum_post AS p ON p.post_thread = t.thread_id
LEFT JOIN user AS u ON u.user_id = p.post_user
ORDER BY t.thread_lastpost DESC LIMIT 0,8
Currently that is returning:
/-----------------------------------------/
| 7049 | USERNAME | Post Entry |
|----------------------------------------|
| 7049 | USERNAME | Post Entry |
|----------------------------------------|
| 7049 | USERNAME | Post Entry |
|----------------------------------------|
| 7049 | USERNAME | Post Entry |
|----------------------------------------|
| 7650 | USERNAME | Post Entry |
|----------------------------------------|
| 7068 | USERNAME | Post Entry |
|----------------------------------------|
| 7056 | USERNAME | Post Entry |
|----------------------------------------|
| 7136 | USERNAME | Post Entry |
I want to remove those first duplicate IDs and only leave one with the latest post entry from that thread.
I hope i have explained it well enough for people to understand.
Thanks.
----------------- EDIT --------------------
Got it to work with GROUP BY:
SELECT t.thread_id, u.user_id, p.post_entry
FROM forum_post AS p
LEFT JOIN forum_thread AS t ON t.thread_id = p.post_thread
LEFT JOIN user AS u ON u.user_id = p.post_user
GROUP BY t.thread_id
ORDER BY t.thread_lastpost DESC LIMIT 0,8
this query may not be best performing, but I can't do much more without knowing structure of your database. You'll need some post_id or post_timestamp for including the second left join as well. Neither DISTINCT nor GROUP BY would solve your issue, as the username and post_entry will usually be different in all cases, i.e. the lines won't actually be distinct.
SELECT t.thread_id, u.user_name, p.post_entry
FROM forum_thread as t
LEFT JOIN forum_post AS p ON p.post_thread = t.thread_id
LEFT JOIN forum_post AS p2 ON p.post_id > p2.post_id
LEFT JOIN user AS u ON u.user_id = p.post_user
where p2.post_id is null
ORDER BY t.thread_lastpost DESC LIMIT 0,8

Using PHP to select and pair two users

I'm trying to make an application that pairs two available users by selecting them randomly from a database and then making them unavailable (so they can't be selected again).
I was wondering how I could select them and then pair them?
I've got the following database structure.
USERS
| id | autoincrement, primary key
| user_id | user's ID
| connected | available, connecting, chatting
ROOMS
| id |
| room_name |
| room_id |
| user_id | first user connected
| user_id2 | second user connected
I'm not making rooms, I'm simply trying to pair users and send them to a room.
Do you mean join the records?
You could do something like:
select * from rooms as r
left outer join users as u on r.user_id=u.id
left outer join users as u2 on r.user_id2=u2.id
where r.id=123
123 = a room id.
EDIT:
Here is a way to update a Rooms record with two random Users
UPDATE rooms
SET
user_id=(SELECT id FROM users ORDER BY RAND() LIMIT 0,1),
user_id2=(SELECT id FROM users ORDER BY RAND() LIMIT 0,1),
where id=123
I have tested this and it seems to work. This will keep you from having duplicate users:
UPDATE rooms
SET
user_id=(SELECT u1.id FROM users as u1 ORDER BY RAND() LIMIT 0,1),
user_id2=(SELECT u2.id FROM users as u2 where user_id <> u2.id ORDER BY RAND() LIMIT 0,1)
where id=123
The SELECT could be something like this:
SELECT user_id
FROM `USERS`
WHERE connected = 'available'
ORDER BY RAND()
LIMIT 2;
You would need to parse the resultset and assign the to user_ids to variables such as
$user_id1
$user_id2
The first UPDATE would be something like this:
UPDATE `USERS`
SET connected = 'connecting'
WHERE user_id IN ($user_id1,$user_id2);
The INSERT would look something like this:
INSERT INTO `ROOMS` (room_name,room_id,user_id,user_id2)
VALUES ($room_name,$room_id,user_id,user_id2);
Finally the last UPDATE
UPDATE `USERS`
SET connected = 'connected'
WHERE user_id IN ($user_id1,$user_id2);
Although you are probably going to want to lock rows I have not grasped that part of database management yet. For you purposes you might want to look into it.
According to me you'll need three tables instead of two to implement your logic.
Consider my db schema
USERS
| id | autoincrement, primary key
| user_id | user's ID
| connected | available, connecting, chatting
ROOMS
| id |
| room_name |
USERCHAT
| id |
| user_id | user's ID
| room_id | room id
To get pair you can execute following query.
SELECT user_id
FROM USERS
WHERE id NOT IN (SELECT user_id FROM USERCHAT)
ORDER BY RAND()
LIMIT 2;
After selecting users you can simply insert them in ROOMS and USERCHAT tables. Using this schema you can also allow multiple users instead of two for chat.

Using a MySQL join

A user can have two types of relationships on my website. Following or Friends. I want to populate each user's news feed with the posts from users they have a relationship with. So far I have this join:
SELECT *
FROM posts a
LEFT JOIN relationships b
ON a.user_id = b.user_2
WHERE b.user_1 = $user_id AND
b.status IN (1,3,4) OR a.user_id = $user_id
ORDER BY a.post_id DESC
b.status is the column from my relationships table that determines the status of the relationship between two users. 1 being following, 3 being friends, 4 being following with a pending friend request. My join works well, except that It it doesn't take into consideration that there is only one row in my 'relationships' table that can represent a friend ship between any two people. There are two rows for following. The first being user 1(mark) following user 2(matt) and user 2(matt) following user 1(mark). But with friendships it is either a row stating one way or the other, with a status of 3. First off, is that smart practice? Or should I have two rows for this type pf relationship as well? Secondly, how can I make my JOIN QUERY also grab posts from users with where there is a relationship between user_1 and user_2 and the status = 3? With no regard to the order user_1 and user_2 are in?
Table Structure:
Posts:
| user_id | post_id | story |
-----------------------------
| 1 | 1 | text. |
-----------------------------
Relationships:
| rel_id | user_1 | user_2| status |
--------------------------------------
| 1 | 1 | 2 | 3 |
--------------------------------------
In your WHERE statement, do something like this so you match either column in the relationship table. The OR gets both of them.
WHERE b.user_1 = $user_id OR b.user_2 = $user_id
Good luck!

Mysql Query from Query results

I am making something like an announcement board that requires readers to acknowledge that they read it, and was wondering if there is a more efficient way of doing this.
I have 3 Tables on MySQL side:
+-----------------+ +-----------------+ +-----------------+
| Announcements | | Acknowledgement | | User |
+-----------------+ +-----------------+ +-----------------+
| announce_id | | ack_id | | user_id |
| announce_msg | | announce_id | | user_name |
| ... | | user_id | | ... |
+-----------------+ +-----------------+ +-----------------+
When a user "reads" the announcement (by clicking a button), Acknowledgment table will be inserted with the Announcement ID and User ID. When a second user "reads" the same announcement, Acknowledgement table will be inserted again with same Announcement ID and the second User ID and so on...
+--------------------------------+
| Acknowledgement |
+--------+-------------+---------+
| ack_id | announce_id | user_id |
+--------+-------------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 4 |
| 3 | 1 | 3 |
| 4 | 3 | 1 |
| 5 | 3 | 6 |
| 6 | 3 | 2 |
+--------+-------------+---------+
Now to the problem. On the front end, when I list all the announcements on a page, I would have to first query for all the announcements. Then, for each announcement, I would have to do another query for all the users that have read this announcement.
$sql = "select * from Announcements";
$result = $pdo->query($sql);
while ($row = $result->fetch())
{
$announce_id = $row['announce_id'];
$announce_msg = $row['annouce_msg'];
$readers = "";
$sql2 = "select u.user_name from Acknowledgement as a INNER JOIN User as u where announcement_id =".$annouce_id;
$result2 = $pdo->query($sql);
while ($row2 = $result2->fetch())
{
$readers .= $row2['user_name'].", ";
}
echo "id:".$annouce_id.", message:".$announce_msg.", Readers:".$readers;
}
So if there 10 announcements on the page, there will be 10 sub-queries for each announcement. What I have now does the job right now... but what if there is 1000 announcements? Then there will be 1000 sub-queries? Sounds like the database will be really hammered. So I'm hoping there is a better way of doing this.
Also, if 1000 people in the user table reads all 1000 announcements, the acknowledgement table will have 1000x1000 entries. seems like the acknowledgement table will become really really long. Will that be a problem as time goes by?
This is a really rough example of what I'm trying to do but it did take me a long time to write all this. If more details is needed let me know.
There is a better way. You can use a single query with group_concat:
select a.*, group_concat(u.user_name separator ', ') as AllUsers
from Announcements a join
Acknowledgement ak
on a.Announce_Id = ak.Announce_Id join
User u
on u.user_ID = ak.User_ID
group by a.announce_id
This uses the MySQL feature of hidden columns to group by only one column (announce_id) but still pull in a bunch of other columns with no aggregations (everything else pulled in by the "*").
If your purpose here is to filter out the announcements that your current user has read, you can do this an entirely different way. Instead of querying for every announcement, and then finding out all the users that have read those announcements and examining those results to find ones that your use has read and trimming them from the displayed list, you can just query in one go for everything a particular user (or list of users) have not yet read.
Change your query to this:
SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID = <INSERT USER ID HERE>)
That should return all Announcement rows that this particular user has not yet acknowledged. If you change that final WHERE clause to be WHERE User_ID IN () then you can specify a list of user IDs.
EDIT: Given the comment you posted above, you could use this query to get all announcements that have been read by no one:
SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID IN (SELECT User_ID FROM User))
The logic for putting together a query to find announcements that haven't been read by someone (if not everyone) is escaping me right this second.
EDIT THE SECOND: Every announcement, and everyone who has and has not read it, requires use of a different kind of join that you've used above, a FULL OUTER JOIN. Unfortunately MySQL doesn't have that feature IIRC, but it can be simulated with a union query
SELECT A.*, ACK.*, U.* FROM Announcements AS A
INNER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
LEFT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID
WHERE U.User_ID IS NOT NULL
UNION ALL
SELECT A.*, ACK.*, U.* FROM Announcements AS A
LEFT OUTER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
RIGHT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID
I think that should do it. No facilities to test at the moment, of course.

Categories