Actually i am trying to create a conversation interface like FB(Messages) and for that a sql query is used to fetch all the persons whom with user is talked already.
I need the id of the user from whom he had talked in descending order,
Like if A has chatted with B and C. Then B AND C will be result of that query and B will come first because A chatted with B recently.
My 'messages' table structure is :
http://www.softnuke.com/me/files/DB.png
This is the FB example:
http://www.softnuke.com/me/files/msg.png
This is my incorrect query which needs to be fixed:
SELECT DISTINCT(`mates`)FROM(
SELECT `time` AS `time`,`from_id` AS `mates`
FROM `messages` AS T WHERE (`from_id`=$uid OR `to_id`=$uid)
UNION
SELECT `time` AS `time`,`to_id` AS `mates`
FROM `messages` AS T WHERE (`from_id`=$uid OR `to_id`=$uid)
) AS T
WHERE `mates`!='$uid'
ORDER BY `time`
$uid will give me the variable of the user I want to fetch List(Here its A).
You seem to be getting the main person and the person they were talking to, irrespective of which one is the main person. Also not quite sure how MySQL will work out the time to order things by when you are using DISTINCT which will remove some of the records with their times.
You could get the max time and order by that:-
SELECT `mates`, MAX(`time`) AS LatestConv
FROM(
SELECT `time` AS `time`,`from_id` AS `mates`
FROM `messages` AS T WHERE `to_id`=$uid
UNION
SELECT `time` AS `time`,`to_id` AS `mates`
FROM `messages` AS T WHERE `from_id`=$uid
) AS T
GROUP BY `mates`
ORDER BY LatestConv
To get the status of that latest message:-
SELECT a.mates, a.LatestConv, IFNULL(b.Status, c.Status)
FROM
(
SELECT mates, MAX(`time`) AS LatestConv
FROM(
SELECT `time` AS `time`, from_id AS mates
FROM messages AS T
WHERE to_id = $uid
UNION
SELECT `time` AS `time`, to_id AS mates
FROM messages AS T
WHERE from_id = $uid
) AS T
GROUP BY `mates`
) a
LEFT OUTER JOIN messages b
ON a.mates = b.from_id AND a.LatestConv = b.`time` AND b.to_id = $uid
LEFT OUTER JOIN messages c
ON a.mates = c.to_id AND a.LatestConv = c.`time` AND c.from_id = $uid
ORDER BY LatestConv
Note that this might get a touch confused if there are multiple messages to the same person which all share the same latest time. If this is likely it could be coped with as follows:-
SELECT a.mates, a.LatestConv, MAX(IFNULL(b.Status, c.Status))
FROM
(
SELECT mates, MAX(`time`) AS LatestConv
FROM(
SELECT `time` AS `time`, from_id AS mates
FROM messages AS T
WHERE to_id = $uid
UNION
SELECT `time` AS `time`, to_id AS mates
FROM messages AS T
WHERE from_id = $uid
) AS T
GROUP BY `mates`
) a
LEFT OUTER JOIN messages b
ON a.mates = b.from_id AND a.LatestConv = b.`time` AND b.to_id = $uid
LEFT OUTER JOIN messages c
ON a.mates = c.to_id AND a.LatestConv = c.`time` AND c.from_id = $uid
GROUP BY a.mates, a.LatestConv
ORDER BY LatestConv
Related
I have two requests
UPDATE :
I need to do something like that :
SELECT poste_nom, ups_type_contrat,
(SELECT `entpro_date`
FROM ENT_PRO
WHERE entpro_user_id = 2
ORDER BY `entpro_id` DESC
LIMIT 1) ,
serv_nom,
serv_id_resp,
user_credit_cpf,
user_indice_salarial,
FLOOR( DATEDIFF( CURDATE( ) , user_dateentree ) /365 ) AS dateEntree
FROM USER
INNER JOIN USER_POSTE_SERVICE
ON USER.user_id= USER_POSTE_SERVICE.ups_poste_id
INNER JOIN POSTE
ON USER_POSTE_SERVICE. ups_poste_id = POSTE.poste_id
INNER JOIN SERVICE
ON USER_POSTE_SERVICE.ups_id_serv = SERVICE.serv_id
WHERE user_id = 2
ORDER BY user_nom ASC
Is it possible to gather two requests in only one ?
From what I understood you want to simple merge the result of your sub-query to your main SELECT, if so you could try it this way:
SELECT poste_nom,
ups_type_contrat,
ENT_PRO_RESULT.entpro_date,
serv_nom,
serv_id_resp,
user_credit_cpf,
user_indice_salarial,
FLOOR( DATEDIFF( CURDATE( ) , user_dateentree ) /365 ) AS dateEntree
FROM USER
LEFT JOIN (SELECT entpro_date,
entpro_user_id
FROM ENT_PRO
ORDER BY entpro_id DESC
LIMIT 1) ENT_PRO_RESULT
ON USER.user_id = ENT_PRO_RESULT.entpro_user_id
INNER JOIN USER_POSTE_SERVICE
ON USER.user_id = USER_POSTE_SERVICE.ups_poste_id
INNER JOIN POSTE
ON USER_POSTE_SERVICE.ups_poste_id = POSTE.poste_id
INNER JOIN SERVICE
ON USER_POSTE_SERVICE.ups_id_serv = SERVICE.serv_id
WHERE user_id = 2
ORDER BY user_nom ASC
I've joined it on:
ON USER.user_id = ENT_PRO_RESULT.entpro_user_id
So you only need to specify the:
WHERE user_id = 2
And the sub-query will use the current row user id for the LEFT JOIN.
I have query like this:
SELECT `all_messages`.`user_1`, `messages`.*, `users`.`username`
FROM `all_messages`
JOIN `messages` ON (`all_messages`.`user_2` = `messages`.`from_user`)
JOIN `users` ON (`all_messages`.`user_2` = `users`.`id`)
WHERE `all_messages`.`user_1` = '12'
ORDER BY `messages`.`sent` DESC LIMIT 2
Now this query does what I need but my problem is with this line
ON (`all_messages`.`user_2` = `messages`.`from_user`)
It selects all data from messages where the matches was found but I need only one newest record. I hope you guys get what I mean.
If you need one "newest record" you should have a date column or something, lets name it "CREATION_TIME", so you could do something like this
SELECT AM.user_1, M.*, U.username
FROM all_messages AM, messages M , users U
WHERE AM.user_1 = '12'
AND AM.user_2 = M.from_user
AND AM.user_2 = U.id
AND M.CREATION_TIME =
(
SELECT MAX(CREATION_TIME)
FROM messages
WHERE from_user= M.from_user
)
ORDER BY M.sent DESC LIMIT 2
Edit
SELECT AM.user_1, M.*, U.username
FROM all_messages AM, messages M, users U
WHERE AM.user_1 = '12'
AND AM.user_2 = M.from_user
AND AM.user_2 = U.id
AND M.sent =
(
SELECT MAX(sent)
FROM messages
WHERE from_user= M.from_user
)
ORDER BY M.sent DESC LIMIT 2
It should work
HI im running socialengine3 and need optimization on the a custom Mutual friends query.
Its currently taking 15 seconds to execute
Friends table
friend_id
friend_user_id1
friend_user_id2
friend_status
friend_type
users
user_id
Edited
I have converted in into exists and still its now executing in 20 seconds.
below is the updated query.
SELECT friendlist.friend_user_id2, se_users.username, se_users.id, se_users.image, se_users.name, se_users.surname, count( * ) AS mutral_friends
FROM `se_friends` friendlist
INNER JOIN `users` se_users ON friendlist.friend_user_id2 = `se_users`.id
WHERE EXISTS (
SELECT se.friend_user_id2
FROM se_friends se
WHERE se.friend_user_id1 = '105012'
AND se.friend_status = '1'
AND se.friend_user_id2 = friendlist.friend_user_id1
) AND NOT EXISTS (
SELECT se1.friend_user_id2
FROM `se_friends` se1
WHERE se1.friend_user_id1 = '105012'
AND friendlist.friend_user_id2 = se1.friend_user_id2
)
AND NOT (
friendlist.friend_user_id2 = '105012'
)
AND friendlist.friend_status = '1'
GROUP BY friendlist.friend_user_id2, se_users.username, se_users.id, se_users.image, se_users.name, se_users.surname
ORDER BY mutral_friends DESC
LIMIT 0 , 20
Orignal query
SELECT DISTINCT `se_friends`.friend_user_id2, se_users.username, se_users.id, se_users.image, se_users.name, se_users.surname, count(*) as mutral_friends
FROM `se_friends`
INNER JOIN `users` se_users` ON `se_friends`.friend_user_id2=`se_users`.id
WHERE
(se_friends.friend_user_id1 <> '30355' or se_friends.friend_user_id2 <> '30355') AND
se_friends.friend_user_id1 IN
(SELECT se_friends.friend_user_id2
FROM `se_friends`
WHERE se_friends.friend_user_id1='".$user_id."' AND se_friends.friend_status='1')
AND `se_friends`.friend_user_id2 NOT IN
(SELECT se_friends.friend_user_id2
FROM `se_friends`
WHERE se_friends.friend_user_id1='".$user_id."'
)
AND NOT(se_friends.friend_user_id2='".$user_id."') AND se_friends.friend_status='1'
GROUP BY `se_friends`.friend_user_id2, se_users.username, se_users.id, se_users.image, se_users.name, se_users.surname
ORDER BY mutral_friends DESC
LIMIT 0, 20
IN is very expensive operation.
try to replace it with EXISTS. eg
select * from table where user_id in (select user_id from users where active='A')
and
select * from table t where exists (select user_id from users u where t.user_id = u.user_id and u.active='A')
if it won't be helpful, it's better to look at execution plan
I'm selecting total count of villages, total count of population from my tables to build statistics. However, there is something wrong. It returns me everything (530 pop (there are 530 pop in total), (106 villages (there are 106 users in total)) in first row, next rows are NULLs
SELECT s1_users.id userid, (
SELECT count( s1_vdata.wref )
FROM s1_vdata, s1_users
WHERE s1_vdata.owner = userid
)totalvillages, (
SELECT SUM( s1_vdata.pop )
FROM s1_users, s1_vdata
WHERE s1_vdata.owner = userid
)pop
FROM s1_users
WHERE s1_users.dp >=0
ORDER BY s1_users.dp DESC
Try removing s1_users from inner SELECTS
You're already using INNER JOINs. Whan you list tables separated with comma, it is a shortcut for INNER JOIN.
Now, the most obvious answer is that your subqueries using aggregating functions (COUNT and SUM) are missing a GROUP BY clauses.
SELECT s1_users.id userid, (
SELECT count( s1_vdata.wref )
FROM s1_vdata, s1_users
WHERE s1_vdata.owner = userid
GROUP BY s1_vdata.owner
)totalvillages, (
SELECT SUM( s1_vdata.pop )
FROM s1_users, s1_vdata
WHERE s1_vdata.owner = userid
GROUP BY s1_vdata.owner
)pop
FROM s1_users
WHERE s1_users.dp >=0
ORDER BY s1_users.dp DESC
However, using subqeries in column list is really inefficient. It casues subqueries to be run once for each row in outer query.
Try like this instead
SELECT
s1_users.id AS userid,
COUNT(s1_vdata.wref) AS totalvillages,
SUM(s1.vdata.pop) AS pop
FROM
s1_users, s1_vdata --I'm cheating here! There's hidden INNER JOIN in this line ;P
WHERE
s1_users.dp >= 0
AND s1_users.id = s1_vdata.owner
GROUP BY
s1_users.id
ORDER BY
s1_users.dp DESC
SELECT s1_users.id AS userid,
(
SELECT COUNT(*)
FROM s1_vdata
WHERE s1_vdata.owner = userid
) AS totalvillages,
(
SELECT SUM(pop)
FROM s1_vdata
WHERE s1_vdata.owner = userid
) AS pop
FROM s1_users
WHERE dp >= 0
ORDER BY
dp DESC
Note that this is less efficient than this query:
SELECT s1_users.id AS user_id, COUNT(s1_vdata.owner), SUM(s1_vdata.pop)
FROM s1_users
LEFT JOIN
s1_vdata
ON s1_vdata.owner = s1_users.id
GROUP BY
s1_users.id
ORDER BY
dp DESC
since the aggregation needs to be done twice in the former.
SELECT userid,totalvillages,pop from
(
SELECT s1_users.id as userid, count( s1_vdata.wref ) as totalvillages
FROM s1_vdata, s1_users
WHERE s1_vdata.owner = userid
GROUP BY s1_users.id) tabl1 INNER JOIN
(
SELECT s1_users.id as userid, SUM( s1_vdata.pop ) as pop
FROM s1_users, s1_vdata
WHERE s1_vdata.owner = userid
GROUP BY s1_users.id) tabl2 on tabl1.userid = tabl2.userid
Ok, I'm trying to base something similar off of this, but not quite getting it nailed: GROUP BY and ORDER BY
Basically, wanting a query to find the latest messages in each 'thread' between the current logged-in user and any other users, but via a flat (non-'threaded') table of messages:
messages {
id,
from_uid,
to_uid,
message_text,
time_added
}
Assuming the current user's uid is '1', and the latest message in each 'thread' could either be from that user, or to that user (the other party always denoted by thread_recipient):
SELECT a.*,thread_recipient
FROM messages a
JOIN (SELECT IF(from_uid = '1',to_uid,from_uid) AS thread_recipient,
MAX(time_added) AS recency
FROM messages
WHERE (from_uid = '1' OR to_uid = '1')
GROUP BY thread_recipient) b ON thread_recipient = (IF(a.from_uid = '1',a.to_uid,a.from_uid))
AND b.recency = a.time_added
ORDER BY a.time_added DESC
But I fear this ain't gonna work right, and maybe messages sent at the same time might end up being returned for the wrong user?
Is my WHERE condition misplaced?
Any wisdom much appreciated.
Here's an idea: take the UNION of the following two queries, then get
the maximum dates from the result.
SELECT id,to_uid AS other_party,time_added FROM messages WHERE from_uid = '1'
SELECT id,from_uid AS other_party,time_added FROM messages WHERE to_uid = '1'
When you do the following:
SELECT MAX(time_added),other_party
FROM (SELECT id,to_uid AS other_party,time_added FROM messages WHERE from_uid = '1'
UNION
SELECT id,from_uid AS other_party,time_added FROM messages WHERE to_uid = '1'
) MyMessages
GROUP BY other_party
You will get the most recent time associated with a message sent to
each person that user '1' is corresponding with. Then you can join the
results of that to the original messages table to get what you want:
SELECT Messages.*
FROM (SELECT MAX(time_added) AS MaxTime,other_party
FROM (SELECT id,to_uid AS other_party,time_added FROM messages WHERE from_uid = '1'
UNION
SELECT id,from_uid AS other_party,time_added FROM messages WHERE to_uid = '1'
) MyMessages
GROUP BY other_party
)
JOIN Messages
ON (Messages.time_added = MyMessages.MaxTime AND
(Messages.to_uid = MyMessages.other_party AND Messages.from_uid = '1' OR
Messages.from_uid = MyMessages.other_party AND Messages.to_uid = '1')
)
Try this - I think it gives you what you are looking for more simply and efficiently.
SELECT latest_time = MAX(a.time_added, b.time_added)
FROM messages a, messages b
LEFT JOIN messages a1
ON a1.from_uid = a.from_id
-- find the case where the following doesn't exist
-- so you know there is nothing after b1
LEFT JOIN messages a2
ON a2.from_uid = a.from_id
AND a2.time_added > a1.time_added
LEFT JOIN messages b1
ON b1.to_uid = b.to_id
LEFT JOIN messages b2
ON b2.to_uid = b.to_id
AND b2.time_added > b1.time_added
WHERE a.from_id = '1'
AND b.to_id = '1'
AND c1.id IS NULL
AND c2.id IS NULL
ORDER BY a.time_added DESC
Ok, after comments - if id is autoincrement use it instead of message time. But you have propoer condition to ensure that messages will not be delivered to wrong persons.