I am new to SQL. I am developing a chat application to get chat conversations like in Facebook. I have written the following query:
select m.toid, m.fmid, m.message, m.seen
from messages m
where ((toid = 5 or fmid = 5))
and not exists (select 1 from messages m2
where greatest (m2.toid, m2.fmid) = reatest(m.toid,m.fmid)
and least (m2.toid, m2.fmid) = least(m.toid,m.fmid)
and m2.message_id > m.message_id )
order by message_id desc
Above query returns as shown below.
Explanation from picture: the last message between 5 and 4 is fghhgj and the message is not seen. Now I want to get number of messages which are not seen between 5 and 4.
I mean I want to get count of seen where seen=0.
In Brief:
Message table contains column called seen. If message is not seen, its value is 0. Now I want to get how many messages are not seen for every conversation.
To count the # of rows where seen is equal to 0 in your result set, put your query inside of a derived table and use select count(*) with where seen = 0
select count(*) from (
select m.toid,m.fmid,m.message,m.seen from messages m
where (toid = 5 or fmid = 5)
and not exists (select 1 from messages m2 where
greatest(m2.toid,m2.fmid) = greatest(m.toid,m.fmid) and
least(m2.toid,m2.fmid) = least(m.toid,m.fmid) and m2.message_id > m.message_id )
) t1 where seen = 0
Edit
Your updated explanation seems to indicate you want the # of unseen messages for every conversation. If so, select all messages where seen equals 0 and group them by participants using greatest and least.
select least(toid,fmid), greatest(toid,fmid), count(*)
from messages
where seen = 0
group by least(toid,fmid), greatest(toid,fmid)
If you also need to include conversations with no unseen messages (i.e. count=0), then remove the where seen = 0 condition and replace count(*) with sum(seen=0), where seen=0 evaluates to either a 1 or 0.
select least(toid,fmid), greatest(toid,fmid), sum(seen=0)
from messages
group by least(toid,fmid), greatest(toid,fmid)
To select additional columns, join messages to a derived table containing the counts:
select m.*, t1.seen_count from messages m
join (
select least(toid,fmid) leastid, greatest(toid,fmid) greatestid, sum(seen=0) seen_count
from messages
group by least(toid,fmid), greatest(toid,fmid)
) t1 on t1.leastid = least(m.toid,fmid) and t1.greatestid = greatest(m.toid,fmid)
If you just want the count where the seen = 0 use:
SELECT COUNT(*) AS TOTAL FROM messages WHERE seen = 0;
OR
within your query only just add COUNT(*) AS TOTAL before specifying the table's name within your query and WHERE seen = 0 in the where clause.
It'll return all the records with seen = 0 but with an extra column TOTAL.
Hope you wanted this. If you need anything else please explain.
Related
The goal is to load a list of chats where the user sending the request is a member in. Some of the chats are group chats (more than two members) and there I want to show the profile pictures from the users who wrote the last three messages.
The first query to load meta data like the title and the timestamp of the chat is:
SELECT Chat_Users.ID_Chat, Chats.title, Chats.lastMessageAt
FROM Chat_Users
JOIN Chats ON Chats.ID = Chat_Users.ID_Chat
GROUP BY Chat_Users.ID_Chat
HAVING COUNT(Chat_Users.ID_Chat) = 2
AND MAX(Chat_Users.ID_User = $userID) > 0
ORDER BY Chats.lastMessageAt DESC
LIMIT 20
The query to load the last three profile pictures from one of the chats loaded with the query above is:
SELECT GROUP_CONCAT(innerTable.profilePictures SEPARATOR ', ') AS 'ppUrls',
innerTable.ID_Chat
FROM
(
SELECT Chat_Users.ID_Chat, Users.profilePictureUrl AS profilePictures
FROM Users
JOIN Chat_Users ON Chat_Users.ID_User = Users.ID
JOIN Chat_Messages ON Chat_Messages.ID_Chat = Chat_Users.ID_Chat
WHERE Chat_Users.ID_Chat = $chatID
ORDER BY Chat_Messages.timestamp DESC
LIMIT 3
) innerTable
GROUP BY innerTable.ID_Chat
Both are working separately but I want to merge them together so I don't have to run the second query in a loop due to performance reasons. Unfortunately I have no idea how this can be achieved because the second query needs the $chatID, which it only gets from the first query.
So to clarify the desired result: The list with the profile picture urls (second query) should be just another column in the result of the first query.
I hope it is explained in a reasonably understandable way. Any help would be much appreciated.
Edit: Sample data from the affected tables:
Table "Chats":
Table "Chat_Users":
Table "Chat_Messages":
Table "Users":
This fufils the brief, however it requires a view because MySQL 5.x doesn't support the WITH clause.
It's long and cluncky and I've tried to shorten it but this is as good as I can get, hopefully someone will pop up in the comments with a way to make it shorter!
The view:
CREATE VIEW last_interaction AS
SELECT
id_chat,
id_user,
MAX(timestamp) AS timestamp
FROM chat_messages
GROUP BY id_user, id_chat
The query:
SELECT
Chat_Users.ID_Chat,
Chats.title,
Chats.lastMessageAt,
urls.pps AS profilePictureUrls
FROM Chat_Users
JOIN Chats ON Chats.ID = Chat_Users.ID_Chat
JOIN (
SELECT
lo.id_chat,
GROUP_CONCAT(users.profilePictureUrl) AS pps
FROM last_interaction lo
JOIN users ON users.id = lo.id_user
WHERE (
SELECT COUNT(*) -- the amount of more recent interactions
FROM last_interaction li
WHERE (li.timestamp = lo.timestamp AND li.id_user > lo.id_user)
) < 3
GROUP BY id_chat
) urls ON urls.id_chat = Chats.id
GROUP BY Chat_Users.ID_Chat
HAVING COUNT(Chat_Users.ID_Chat) > 2
AND MAX(Chat_Users.ID_User = $userID)
ORDER BY Chats.lastMessageAt DESC
LIMIT 20
How can you please help me I have a table in the database with a column called "recioient_id"
The value of its rows is repeated more than once How do I prevent repeating rows while keeping the order descending and displaying the last row
I have tried so much without a find
I did this query and was good at preventing repetition and did not display the last row
Query which was:
SELECT *
FROM `messages`
WHERE `sender_id` = 1
GROUP BY `recioient_id` DESC
HAVING COUNT(*) >= 1
Here is a sample table
I hope to be successful in translation and hope to help
If you want one row per recioient_id, use filtering. I would recommend a correlated subquery:
SELECT m.*
FROM messages m
WHERE m.sender_id = 1 AND
m.messages_id = (SELECT MAX(m2.messages_id)
FROM messages m2
WHERE m2.sender_id = m.sender_id AND
m2.recioient_id = m.recioient_id
)
ORDER BY m.recioient_id;
I want to get '0' when no records found on execution of sql query. I try so many things like ifnull(count(*),0) but not gives me the result which i want.
Query is mention as below :
SELECT chat_room_id,COUNT(*) FROM chat WHERE sender_id=13 GROUP BY chat_room_id
It gives me the result as below:
__________________________
| chat_room_id | Count(*) |
--------------------------
It not return any 'null' or '0' , so i want to get '0' OR 'null' if no records found.
As long as there is a table Chats with field sender_id then the following will return a row even if the sender_Id 13 does not exist in the table.
set #sender = 13;
set #chats = (select count(*) from Chat where sender_id = #sender);
select #sender AS sender_id, IFNULL(#chats,0) AS Count_chats
;
See this working at SQLFiddle
AFTER YOUR EDIT CHANGES
Try this:
SELECT distinct c1.chat_room_id,
(select count(*) from chat c2 c2.chat_room_id = c1.chat_room_id and c2.sender_id = 13)
FROM chat c1
Pay attention:
A table named CHAT I suppose contains all chats in your DB and no the messages and senders.
Instead if you have a table named CHAT_ROOM with the list of all chats so your query becomes:
SELECT chat_room_id,
(SELECT COUNT(*)
FROM chat
WHERE chat.chat_room_id = chat_room.chat_room_id AND chat.sender_id = 13)
FROM chat_room
You can not query data which is not in the source tables or which is not available with calculation.
If you have a table which contains all available chat rooms, you can use that table to get the desired record with a simple query using LEFT OUTER JOIN
SELECT
CR.chat_room_id,
COUNT(C.chat_room_id)
FROM
chat_rooms CR
LEFT JOIN chat C
ON CR.chat_room_id = C.chat_room_id
AND C.sender_id = 13 -- Moved from WHERE to keep all non-matching records from CR
GROUP BY
CR.chat_room_id
If you do not have a specific table, you can use LEFT OUTER JOIN and a subquery to get the result:
SELECT
CR.chat_room_id,
COUNT(C.chat_room_id)
FROM
(SELECT DISTINCT chat_room_id FROM chat) CR
LEFT JOIN chat C
ON CR.chat_room_id = C.chat_room_id
AND C.sender_id = 13 -- Moved from WHERE to keep all non-matching records from CR
GROUP BY
CR.chat_room_id
SQLFiddle demo for the second query
Are you looking to return all the chat_room_ids in case the sender id does not exist?
like, if say sender_id 9 does not exist, what are you looking to return?
In case you wanted something like
chat_room_id count
NO_RECORDS 0
then the below query will work:
declare #Chat_Room_Id varchar(3),
#Count int
select #Chat_Room_Id = cast(chat_room_id as varchar(3)), #Count = count(*) from chat where sender_id=13 group by chat_room_id
select coalesce(#Chat_Room_Id, 'NO RECORDS') chat_room_id, coalesce(#Count, 0) count
In case I did not understand, can you please clarify further?
-hth
Sourav
you can use mysql_affected_rows(); method to get the details of rows affected with your query.
check this
and this
You can put the sql in a sub query this will always return a row
select sub.id,sub.counter from (select sender_id as id,count(*) as counter from chat where sender_id = 13) sub
This will return 0 if there are no records or 1 if there are.
I have dialogs and messages. The goal is to select dialogs and their count of unreaded messages. Table messages has unread field to detect it.
I've tried this
SELECT *, count(unread) as nums
FROM dialogs JOIN messages ON dialogs.id=messages.dialog_id
GROUP BY dialogs.id
HAVING count(unread) <> 0
but always get total count of messages
If "unread" is, as you state, a count of unread messages, then it always has a value, even if zero, and is always included in COUNT(unread).
Try inserting a WHERE messages.unread != 0
If you say that the table messages have a column unread, then I don't get why you should count that column, you probably need to sum those values (assuming that there can be more than one record with the same dialog_id on that table). So, I would write your query like this:
SELECT *
FROM dialogs D
INNER JOIN (SELECT dialog_id, COUNT(*) AS nums
FROM messages
WHERE unread = 1
GROUP BY dialog_id) M
ON D.id = M.dialog_id
WHERE M.nums > 0
Oh!!! Count by messages.id.
Try this:
SELECT *, count(messages.id) as nums FROM dialogs
JOIN messages ON dialogs.id=messages.dialog_id
WHERE messages.unread = 1
GROUP BY dialogs.id
HAVING nums <> 0
An Enum type for messages.unread might need to have it quoted:
SELECT *, count(messages.id) as nums FROM dialogs
JOIN messages ON dialogs.id=messages.dialog_id
WHERE messages.unread = '1'
GROUP BY dialogs.id
HAVING nums <> 0
I'm not sure why you'd create a enum('0','1') though....
I have a query
SELECT m1.mid mid, m1.uid uid, m1.date, m1.body body
FROM messages m1
WHERE m1.chat_id IS NULL
and deleted = 0
AND m1.date in
(
SELECT MAX(m2.date)
FROM messages m2
WHERE m2.uid = m1.uid
AND m2.chat_id IS NULL
and m2.deleted = 0
)
This query was not actually written by me, I got help here - hanks them a lot.
But I have a problem. When there is more than one message with the same date and uid(from the same user) values, i get two or more records with the same date and uid, but i'd like to have only one( no matter which, or the message with the greatest mid f.e.)
I've tried to use distinct(uid) with no success result. Is there any other way to achive the result i want?
You can use this solution to find the most recent message per uid:
SELECT b.*
FROM
(
SELECT MAX(mid) AS maxmid
FROM messages
WHERE chat_id IS NULL
GROUP BY uid
) a
INNER JOIN messages b ON a.maxmid = b.mid
Rather you could add an extra column "count" and display the number of messages from a user on the same day!!
By this you can avoid, getting duplicate records!