I am building a mysql based chat application.
My database schema has the following tables,
Users Messages
================= =================
id id
screen_name message
from
to
timestamp
The from and to fields on the messages table contain the id's of the users that sent and received each message.
I am trying to display all messages between a user ($id) and one of their friends ($friend). My query is the following:
$query = "SELECT messages.* , users.screen_name FROM users CROSS JOIN messages ";
$query .= "ON ( messages.to = $id AND messages.from = $friend ) ";
$query .= "OR ( messages.to = $friend AND messages.from = $id )";
The problem is that every message is twice in the result table.
I tried using DISTINCT but it either doesn't work in this scenario or I used it wrong.
What should my query be in order to have each message between the two users only once?
Something like this should do the trick:
SELECT
messages.*,
users_from.screen_name AS from_screen_name,
users_to.screen_name AS to_screen_name
FROM
messages
JOIN users AS users_from ON messages.from = users_from.id
JOIN users AS users_to ON messages.to = users_to.id
WHERE
(messages.to = $id AND messages.from = $friend)
OR ( messages.to = $friend AND messages.from = $id)
What this does is joing the "users" table twice, once on the "to" column and the second time on the "from" column.
#Travesty3 has already suggested that the DISTINCT keyword will only exclude duplicate rows where all fields are equal to another row. Therefore, the DISTINCT keyword is not the way to go here.
What you can do, however, is to simply GROUP BY messages.id in order to get only one row per message ID (there is no guarantee, however, as to which of the two rows will be excluded).
Related
How would I go about deleting a row from the table 'subjects' that has a primary id 'subject_id' based on the number of rows in another table named 'replies' that uses a 'subject_id' column as a reference.
Example in pseudo code:
If ('subject' has less than 1 reply){
delete 'subject'}
I don't know much about SQL triggers so I have no clue if I would be able to incorporate this directly in the database or if I'd have to write some PHP code to handle this...
To delete any subjects that have had no replies, this query should do the trick:
DELETE s.* FROM subjects AS s
WHERE NOT EXISTS
(
SELECT r.subject_id
FROM replies AS r
WHERE r.subject_id = s.subject_id
);
Demo: DB Fiddle Example
One of the MySQL gurus will need to weigh in on whether or not you can do this directly, but in PHP you could...
$query = "SELECT subject_id FROM subjects WHERE subject='test'";
$return = mysqli_query($mysqli, $query);
$id = mysqli_fetch_assoc($return);
$query = "SELECT reply_id FROM replies WHERE subject_id='".$id[0]."'";
$return = mysqli_query($mysqli, $query);
if(mysqli_num_rows($return) < 1){
$query = "DELETE FROM subjects WHERE subject_id='1'";
$return = mysqli_query($mysqli, $query);
}
This example assumes the "subject" is unique. In other words, SELECTing WHERE subject='test' will only ever return one subject_id. If you were doing this as a periodic cleaning, you would grab all the subject_id values (no WHERE clause) and loop through them to remove them if no replies.
You can achieve this in one query by selecting all (unique) subject-ids from the replies table, and delete all subjects that doesn't have a reply in there. Using SELECT DISTINCT, you don't get the IDs more than once (if a subject has more than one reply), so you don't get unnecessary data.
DELETE FROM subjects
WHERE subject_id NOT IN (SELECT DISTINCT subject_id FROM replies)
Any subject that doesn't have a reply should be deleted!
So you want to delete all subjects with no replies:
DELETE FROM subjects WHERE subject_id NOT IN
(SELECT subject_id FROM replies);
I think this is what you want...
Could someone help me make this better?
I am trying to get all rows from both tables where the sender and recipient matches the user_id and trashed has the value of 1;
This is what I've done. But my code is returning only one row, whereas there are at least two rows: one on each table.
Thanks.
$SQL = "SELECT *
FROM inbox
JOIN outbox
ON inbox.recipient = outbox.sender
AND
inbox.trashed = outbox.trashed
WHERE inbox.recipient = '$user_id'
AND
inbox.trashed = '1'"
I'm making a notifications widget on my website and I'm trying to make it so that if a notification is marked as read, that notification ID will be inserted into another table (table 'b') along with their username so that it is marked as read. Now the problem that I run into is when displaying all notifications (whether they're read or unread) I don't know how to indicate if the notification exists in the secondary table
The currently SQL query is as follows:
$qry = "SELECT * FROM notifications WHERE (notif_recipient = '$user') ORDER BY notif_date DESC";
What I'd like to do is make the query much more complex in order to indicate if a notification exists in another table, so something along the lines of:
$qry = "SELECT notif_id,notif_message,(CASE SELECT notif_is_read AS '1' WHERE notif_id.notifications = notif_id.notifications_read ELSE SELECT notif_is_read AS '0') FROM notifications WHERE (notif_recipient = '$user') ORDER BY notif_date DESC";
Is something like this possible or is it as preposterous as my lack of ability for writing SQL queries
As Marc B had suggested, I decided to use LEFT JOIN in order to identify which messages currently exist in the secondary table, with my query looking as such:
$qrytest = "SELECT notifications.notif_id,notifications.notif_message,notifications.notif_flag,notifications.notif_poster,notifications.notif_date,notifications_read.notif_read_count FROM notifications LEFT JOIN notifications_read ON notifications.notif_id=notifications_read.notif_id WHERE ((notif_recipient = 'all') OR (notif_recipient = '$id')) ORDER BY notif_date DESC,notif_flag DESC";
This in turn will return the results of my primary table (notifications) and if the same notification id exists in my secondary table, I decided to echo out that table's ID count, if the entry does not exist it will simply return as null
$exists = $row['notif_read_count'];
if ($exists !== ''){
// When NOT returning as null do something
} else {
// When exist returns as null do something
}
I'm creating a feed by retrieving information from my database using nested while loops (is there a better way to do this?).
I have one table called users with all the names amongst other things. The other table is called messages which has messages, the user who posted it, and a timestamp.
$userQuery = mysql_query("SELECT name FROM users");
while ($user = mysql_fetch_array($userQuery, MYSQL_NUM)) {
$messageQuery = mysql_query("SELECT message FROM messages WHERE user = $user ORDER BY timestamp DESC");
while ($message = mysql_fetch_array($messageQuery, MYSQL_NUM)) {
echo "$user[0]: $message[0]";
}
}
The problem is that it doesn't order by the timestamp and I can't tell how it's ordered. I've tried timestamp, datetime, and int types with UNIX timestamps.
EDIT: I should add that the user and message matches up fine, it's just the ordering that doesn't work.
I guess you get your users in more or less random order and "within" one user the sorting is ok?!
use:
$result = mysql_query('select users.name,messages.message from messages join users on (users.name=messages.user) order by messages.timestamp');
while($row = mysql_fetch_row($result))
echo "$row[0]: $row[1]";
That should give you an ordered result (at least if you have a column called messages.timestamp. Check the name ;-)). And all in one query...
For the query, you could create a join
SELECT u.name as name, m.message as message
FROM users u inner join messages m
on u.user = m.user
order by
m.timestamp DESC
As for the second part, I don't see anything wrong with your could. May be you could post some samples of your data to see if that is making any difference.
I have a problem with (for me to complicated) MySql query.
Okay, here is what I need to do:
First I need to check messages that some specific user received
$mid=$_SESSION['user'];
$stat1=mysql_query("SELECT id, fromid, toid, subject FROM messages WHERE
toid = '".$mid."' AND subject != 'not readed' GROUP BY fromid ")
or die(mysql_error());
while ($h = mysql_fetch_array($stat1))
{
$whosend=$h['fromid'];
Second thing that I need to do is check the status of the users (deleted or not) who sent the messages ("fromid") to my specific user ("toid"). This I must do from another table:
$stat2=mysql_query("SELECT id, status FROM members WHERE id='".$whosend."' AND
status ='1'")or die(mysql_error());
while ($s = mysql_fetch_array($stat))
{
Then my problems begin to show up.
How can I get the number of the users who sent messages to my specific user with status =1? Not the number of the messages but the total number of the users who sent them.
Is there any easier way to do this query? I tried with join tables like
$stat=mysql_query("SELECT memebers.id, memebers.status, messages.toid,
messages.fromid,messages.subject,messages.id FROM members, messages
WHERE messages.toid='".$mid."' AND members.status ='7' ....
But even in this query I need to have id's of the user who sent messages before this query so there will be another query before this join tables.
So you are looking for the number of members with status = 1 that sent this other member ($mid) a message?
Something like this?
$sql = "select count(distinct messages.fromid) From messages Inner Join members on members.id = messages.fromid Where messages.toid = '" . $mid . "' AND members.`status` = 1";
SELECT id, fromid, toid, subject FROM messages WHERE toid = '".$mid."' AND subject != 'not readed' GROUP BY fromid
can't be right as you have GROUP BY formid and no aggregation in the select clause.