Getting the last message between users like facebook - php

I have a messaging app that I want to display messages like facebook, it should get the last send message by either the sender or recipient, my table layout is like this:
messages_tbl
__________________________________________________________________________
|id | user1Fk| user2Fk |subject | user1Delete | user2Delete | dateCreated |
and my user_tbl
_______________________________________
| id | first_name | last_name | image |
my Query
SELECT `id` , `user1Fk` as sender_id,
(SELECT concat(first_name,\" \",last_name)
FROM user_tbl
WHERE user_tbl.id = sender_id
) as senderName,
`user2Fk` as recipient_id ,
(SELECT concat(first_name,\" \",last_name)
FROM user_tbl
WHERE user_tbl.id = recipient_id
) as recipientName,
(SELECT image
FROM user_tbl
WHERE user_tbl.id = sender_id
) as senderImage,
(SELECT image
FROM user_tbl
WHERE user_tbl.id = recipient_id
) as recipientImage,
`subject`, `message`, `user1Delete`, `user2Delete`,
`dateCreated`
FROM `message_tbl`as m1
WHERE dateCreated = (SELECT MAX(m2.dateCreated)
from message_tbl as m2
WHERE (m1.user1Fk = m2.user1Fk
AND m1.user2Fk = m2.user2Fk
OR m1.user1Fk = m2.user2Fk
AND m1.user2Fk = m2.user1Fk
)
) AND ? IN (m1.user1Fk, m1.user2Fk)
ORDER BY dateCreated DESC
This query is working for the most part but its lacking, I want it to check, if the given id by the ? matches user1Fk it should then check if user1Delete is a 0 or 1, if its a 1 then do not display the message user 1 deleted it, the same with user 2 but I cannot think of the logic, can anyone help me?

I'm assuming that the question mark is there because this is a prepared query and the question mark should be replaced with a user id.
Your query is a little bit over complicated because there are a lot of subselects. I tried to improve it a bit for better performance and to be able to extend it easier. With SQL you can join tables instead of using subqueries, this is better for performance, and it makes it easier to add conditions to the joined tables.
In the example below I added a condition for both the user1 and user2 delete fields to be 0. I set the limit to 1, which means only the first row will be returned. Together with the descending order of the date, this means that only the newest message will be returned. I also changed the question mark to :user_id because in this example it is used twice and this way you'll only have to add it once.
SELECT
m.id,
u1.id AS sender_id,
CONCAT(u1.first_name, ' ', u1.last_name) AS senderName,
u2.id AS recipient_id,
CONCAT(u2.first_name, ' ', u2.last_name) AS recipientName,
u1.image AS senderImage,
u2.image AS recipientImage,
m.subject,
m.message,
m.user1Delete,
m.user2Delete,
m.dateCreated
FROM messages_tbl m
INNER JOIN user_tbl u1 ON m.user1Fk = u1.id
INNER JOIN user_tbl u2 ON m.user2Fk = u2.id
WHERE (m.user1Delete = 0 AND u1.id = :user_id)
OR (m.user2Delete = 0 AND u2.id = :user_id)
ORDER BY m.dateCreated DESC
LIMIT 1
I haven't been able to test it but I hope this is what you were looking for.

Related

Join with condition with different column

I have following two tables :
user_profile
id | name | email | phone
1 | Rahul |r#gmail.com |1234567890
2 | Rohan |r1#gmail.com |1234567890
3 | Mohan |m#gmail.com |1234567890
user_request_table
id | from_user_id|to_user_id|status
1 | 1 | 2 | 2
2 | 3 | 1 | 2
Here status
0 = Request is cancel,
1 = request is pending,
2 = request accepted
I have following section to display :
Find a friend
Here I need to display suggetion for friend. I need to display all record from user_profile table but user should not be a friend(status should not be 2) or request should not be pending(status should not be 1).
So if I logged in as Rohan(id : 2) it should suggest me Mohan as friend suggestion.
Would this work:
SELECT
u.*
FROM
user_profile u
LEFT JOIN user_request_table r ON u.id=r.to_user_id AND r.from_user_id = <your_logged_in_user_id>
WHERE
r.status NOT IN (2,1) --filter pending and accepted here ;
So you want those records in user_profile table that do not have a record in user_request table where user is either from_user_id or to_user_id. The pattern is to do a left join to user_request and filter out those where user_request.id is null:
select
p.*
from
user_profile as p
left join user_request as r on
(
(p.id = r.from_user_id and r.to_user_id = {id})
or
(p.id = r.to_user_id and r.from_user_id = {id})
)
and r.status in (1,2)
where
r.id is null
and p.id <> {id}
;
with {id} a parameter for the user you want to suggest friends for.
First get all possible pairs from the user_profile table. Next filter through all the pairs and eliminate the unqualified using data from the user_request_table. It will get all the eligible (from_user , to_user)pairs.
select u1_id as fromuser,u2_id as touser
from
(select distinct u1.id as u1_id,u2.id as u2_id from user_profile u1 cross join user_profile u2 where u1.id!=u2.id) all_pair
where not exists
(select from_user_id ,to_user_id from user_request_table where status>0 and (
((u1_id=from_user_id) and(u2_id=to_user_id)) or ((u1_id=to_user_id) and(u2_id=from_user_id))
)
)
;
The result set looks like this:
fromuser, touser
3 2
2 3
This solution is for MSSQL SERVER, you can modify as per your preferred server
Pass your logged in id to declared variable
DECLARE #LoginId AS INT
SET #LoginId=2 /*here I passed logged in id as 2*/
SELECT * FROM user_profile up
WHERE 1=1 and id<>#LoginId
AND id NOT IN (SELECT from_user_id FROM user_request_table WHERE to_user_id=#LoginId and STATUS in(1,2))
AND id NOT IN (SELECT to_user_id FROM user_request_table WHERE from_user_id=#LoginId and STATUS in(1,2))

In MySQL, how do I select a result where the result contains every value I test for?

Check out this SQL Fiddle for a simplified version of my issue http://sqlfiddle.com/#!9/cf31d3/1
I have 2 tables - chat messages and chat recipients that look like this:
Sample ChatMessages data:
Sample ChatRecipients data:
Basically I want to query only messages that contain a set of user IDs - for example, show only messages exchanged between Bob, Susan, and Chelsea. If I pull up a new chat window with user IDs (1, 2, 3) what is the best way to get messages ONLY involving those 3 people?
Here's a simplified version of my current query (which does not produce the correct result):
SELECT
cm.message_id as 'message_id',
cm.from_id as 'from_id',
(SELECT u.user_fname as 'fname' from Users u where u.user_id = cm.from_id) as 'firstName',
(SELECT u.user_lname as 'lname' from Users u where u.user_id = cm.from_id) as 'lastName',
cm.chat_text as 'chat_text'
FROM
ChatMessages cm
INNER JOIN
ChatRecipients cr
ON
cm.message_id = cr.message_id
INNER JOIN
Users u
ON
cm.from_id = u.user_id
WHERE
cm.from_id in ('1', '2', '3')
AND
cr.user_id in ('1', '2', '3')
I understand that using the 'IN' operator is not correct for this situation, but I'm a bit stuck. Thanks to anyone willing to help!
EDIT:
My sample output returns every row of data that any of the aforementioned user IDs are contained in and looks like this:
My goal is to limit the output to only messages where EVERY user ID I test for is associated with a message_id. For example, if message_id 32 is FROM user_id 7 and TO user_id(s) 11 & 3, I want to retrieve that record. Conversely, if message_id 33 is FROM user_id 7 and to user_id(s) 11 & 4 I do not want to retrieve that record.
The problem here is that your message must either be:
from user 1 and received by 2, 3, ...N
from user 2 and received by 1, 3, ...N
...
from user N and received by 1, 2, ...N-1
and you need a query capable of scaling reasonably, i.e., no single JOIN for every recipient or things like that.
Let's start with the "from" part.
SELECT m.* FROM ChatMessages AS m
WHERE from_id IN ($users)
Now I need to know what recipients these messages have.
SELECT m.* FROM ChatMessages AS m
JOIN ChatRecipients AS r ON (m.message_id = r.message_id)
WHERE from_id IN ($users)
Recipients may be good or bad and I'm interested in how many they are. So
SELECT m.*,
COUNT(*) AS total,
SUM(IF(user_id IN ($users), 1, 0)) AS good
FROM ChatMessages AS m
JOIN ChatRecipients AS r ON (m.message_id = r.message_id)
WHERE from_id IN ($users)
GROUP BY m.message_id;
Finally
A message is acceptable if it's between my [1...N] users, which means that
it has exactly N-1 recipients, N-1 of them good.
SELECT m.*,
COUNT(*) AS total,
SUM(IF(user_id IN ({$users}), 1, 0) AS good
FROM ChatMessages AS m
JOIN ChatRecipients AS r ON (m.message_id = r.message_id)
WHERE from_id IN ({$users})
GROUP BY m.message_id
HAVING total = good AND good = {$n}
Test
In this case with three id's we have $users = 1,2,3 and $n = 2
SELECT m.*,
COUNT(*) AS total,
SUM(IF(user_id IN (1,2,3), 1, 0)) AS good
FROM ChatMessages AS m
JOIN ChatRecipients AS r ON (m.message_id = r.message_id)
WHERE from_id IN (1,2,3)
GROUP BY m.message_id
HAVING total = good AND good = 2
message_id from_id chat_text
1 2 Message from Susan to Bob and Chelsea
2 3 Message from Chelsea to Bob and Susan
3 1 Message from Bob to Chelsea and Susan
add:
'GROUP BY message_id HAVING COUNT(DISTINCT cr.user_id)=2'
The general case in php instead of 2: count($otherUserIds)
See it in action: http://sqlfiddle.com/#!9/bcf1b/13
See also some explanation: Matching all values in IN clause
Answering your question:
If I pull up a new chat window with user IDs (1, 2, 3) what is the
best way to get messages ONLY involving those 3 people?
You can use the following query:
SELECT q_ur.user_fname, q_ur.user_lname, q_cm.chat_text
FROM Users q_ur INNER JOIN
ChatMessages q_cm
ON q_ur.user_id = q_cm.from_id
WHERE q_cm.message_id in (
SELECT cr.message_id FROM ChatMessages cm INNER JOIN
ChatRecipients cr
ON cm.message_id = cr.message_id
WHERE cm.from_id IN (1,2,3)
AND cr.user_id IN (1,2,3)
group by cr.message_id
having count(*) = 2)
Expressions: cm.from_id IN (1,2,3) AND cr.user_id IN (1,2,3) filter messages related to people in the same chat. To filter messages related
to people 1 <-> 2 and 1 <-> 3 and 2 <-> 3 I have user having count(*) = 2. 2 used for filtering all messages whose destinations < or > then
number of people in chat - 1.
So to use this query you have to specify two parameters (in three places): the first parameter is ID of people in the same chat, the second
is the number of people - 1 in this chat.
And you will not retrieve other charts with three people in them where only one of this (1,2,3) triple are participating. To make sure checkout following link:
SQL Fiddle to test query.
Your reasoning seems sound. I have a simplified version of your query which seems to work:
SELECT
ChatMessages.message_id,
ChatMessages.from_id,
Users.user_fname,
Users.user_lname,
ChatMessages.chat_text,
ChatRecipients.user_id as 'to_id'
FROM ChatMessages
INNER JOIN Users
ON ChatMessages.from_id=Users.user_id
INNER JOIN ChatRecipients
ON ChatRecipients.message_id=ChatMessages.message_id
WHERE ChatMessages.from_id IN (1, 3, 4)
AND ChatRecipients.user_id IN (1, 3, 4);
Check the SQLFiddle here to see it working. Your use of the IN clause is fine, but you shouldn't be putting the quotes in there because it is an integer, not a string you are matching.
For this type of scenarios, I would better suggest a different type of database structure to create a message thread with all the users involved and instead of connecting each messages to each users, connect them to the threads. Here are the sample tables:
MessageThreads
| thread_id | created_at |
-----------------------------------
| 1 | 2016-01-20 18:24:36 |
| 2 | 2016-01-20 19:24:24 |
ThreadRecipients
| thread_id | user_id | last_read_message |
-----------------------------------------------
| 1 | 1 | 2 |
| 1 | 2 | 3 |
| 1 | 3 | 1 |
ChatMessages (like before)
| message_id | from_id | chat_text | chat_datetime |
---------------------------------------------------------------------
| 1 | 1 | Test | 2016-01-20 18:24:36 |
| 1 | 1 | Test2 | 2016-01-20 19:24:36 |
| 1 | 2 | Test3 | 2016-01-20 19:34:36 |
ThreadMessages
| thread_id | message_id |
---------------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
Here, inplace of isRead field in your ChatRecipients table, I have used last_read_message in ThreadRecipients table, where you can always update with the last seen message of the user in the thread. But if you want to still keep a such track of each message to each user, you can still have another table with only message_id and user_id where data will be inserted only if the message is read by the user. (And You can still use your ChatRecipients table for one to one messages, if you don't want to create thread in that case. )
Why It's necessary
It's because if you use ChatRecipients table you are adding multiple rows to ChatRecipients table for each message, in the long run its going to cost you some extra space. But if you use ThreadMessages as I suggested, you will put only one row per message in ThreadMessages and users will be connected to threads through ThreadRecipients table which will be one row per user per thread.
For example, if you have thread of 100 users with 50 messages, in your approach you will have 50 x 100 rows in ChatRecipients table. But with this approach, it will be 100 rows in ThreadRecipients table and 50 rows in ThreadMessages table. Just think about the difference.
How to Insert Data
So, when you have a new message thread between a group of persons. As for your example, we have three users with ID 1, 2, 3.
Insert a new thread to ThreadRecipients table. Get the new thread_id. (It can be an auto incremented value)
Now for each associated user_id, insert one row in ThreadRecipients table. For example, We have thread_id 3 and user_id 1, 2 , 3.
INSERT INTO ThreadRecipients (thread_id, user_id) VALUES(3, 1), (3, 2), (3, 3)
Now, when any person send messages to the thread, just insert the row to ChatMessages table (like before), get the message_id and Insert a new row to ThreadMessages with thread_id and message_id. For example our message_id = 9.
INSERT INTO ThreadMessages (thread_id, message_id) VALUES(3, 9)
When anyone reads the message, just update the last_read_message for the user in the ThreadRecipients table with the read message_id (the condition last_read_message < 3 makes sure that, the message you are updating with isn't older than the existing last_read_message).
UPDATE ThreadRecipients SET last_read_message = 3 WHERE user_id = 2 AND thread_id = 3 AND last_read_message < 3
Note: Always before inserting new thread, check if a thread already exists with the same users so that you don't have duplicate thread for the same group of users. (See below for how to find existing thread for specific users).
How to Get Messages
Now, your query should only check if there is a thread involving the specific users and no other users are involved in the thread. So, in WHERE clause
First we have a sub query SELECT COUNT(*) FROM ThreadRecipients WHERE user_id in ('1', '2', '3') AND thread_id = tm.thread_id) and we are checking if that equals 3. It will be 4, if the number of users is 4 and so on. (Keep a UNIQUE key for thread_id + user_id, so that there can never be a data duplication and get an incorrect count match thereby).
The other condition makes sure, there is no other user involved so we are just checking if any row exists WHERE NOT user_id IN ('1', '2', '3') AND thread_id = tm.thread_id). If exists, we will just consider it as another thread involving more persons.
So, finally the query can be like this : (See the SQL Fiddle)
SELECT
cm.message_id as 'message_id',
cm.from_id as 'from_id',
(SELECT u.user_fname as 'fname' from Users u where u.user_id = cm.from_id) as 'firstName',
(SELECT u.user_lname as 'lname' from Users u where u.user_id = cm.from_id) as 'lastName',
cm.chat_text as 'chat_text'
FROM
ChatMessages cm
INNER JOIN
ThreadMessages tm
ON
cm.message_id = tm.message_id
INNER JOIN
Users u
ON
cm.from_id = u.user_id
WHERE
(SELECT COUNT(*) FROM ThreadRecipients WHERE user_id in ('1', '2', '3') AND thread_id = tm.thread_id) = 3
AND NOT EXISTS(select NULL FROM ThreadRecipients WHERE NOT user_id IN ('1', '2', '3') AND thread_id = tm.thread_id)
You can try this
SqlFiddle Demo
SELECT
cm.message_id as 'message_id',
cm.from_id as FromID,
cr.user_id as ToID,
(SELECT CONCAT(user_fname," ",user_lname) from Users where Users.user_id=cm.from_id ) as 'sender_name',
(SELECT CONCAT(user_fname," ",user_lname) from Users where Users.user_id=cr.user_id ) as 'recipient_name',
cm.chat_text as 'chat_text'
FROM ChatRecipients cr
INNER JOIN ChatMessages cm ON cm.message_id = cr.message_id
WHERE cr.user_id in (1, 2, 3)
and cm.from_id in (1, 2, 3)
GROUP BY cr.user_id
HAVING COUNT(cr.user_id)>=2
Thanks to everyone who provided an answer. #Iserni has answered my question correctly I believe, although I do think the second argument in the WHERE clause as I've posted below is necessary. None of the test cases in my SQL Fiddle example would have caused Iserna's query to produce an incorrect result though, so that's on me.
I was actually able to solve my problem a few hours prior to seeing Iserna's solution, so I figured I'd post what worked for me in case it can help anyone:
SELECT
cm.message_id as 'message_id',
cm.from_id as 'from_id',
(SELECT u.user_fname as 'fname' from Users u where u.user_id = cm.from_id) as 'firstName',
(SELECT u.user_lname as 'lname' from Users u where u.user_id = cm.from_id) as 'lastName',
cm.chat_text as 'chat_text',
(SELECT COUNT(DISTINCT cr.user_id) as 'uid' FROM ChatRecipients cr WHERE cr.message_id = cm.message_id) as 'countDistinct'
FROM
ChatMessages cm
INNER JOIN
ChatRecipients cr
ON
cm.message_id = cr.message_id
INNER JOIN
Users u
ON
cm.from_id = u.user_id
WHERE
cm.from_id in ('1', '2', '3')
AND
cr.user_id in ('1', '2', '3')
GROUP BY
cm.message_id
HAVING
countDistinct = 2
AND
COUNT(DISTINCT cr.user_id) = 2
They key to solving this problem is that you must count the number of distinct message recipients, which must be equal to N-1 of the total number of people involved in the message. You must also count the number of user_id's that you supply the query and make sure that that you only get values for messages intended for N-1 of the users represented. That sort of double checking logic makes this problem somewhat difficult.
Here's what this query looks like in a real scenario with dynamic input if anyone is interested.
SELECT
DISTINCT cm.message_id as 'message_id',
cm.from_id as 'from_id',
(SELECT u.user_fname as 'fname' from Users u where u.user_id = cm.from_id) as 'firstName',
(SELECT u.user_lname as 'lname' from Users u where u.user_id = cm.from_id) as 'lastName',
cm.chat_text as 'chat_text',
cm.chat_datetime as 'datetime',
(SELECT COUNT(DISTINCT cr.user_id) as 'uid' FROM ChatRecipients cr WHERE cr.message_id = cm.message_id) as 'countDistinct'
FROM
ChatMessages cm
INNER JOIN
ChatRecipients cr
ON
cm.message_id = cr.message_id
INNER JOIN
Users u
ON
cm.from_id = u.user_id
WHERE
cm.from_id in ('$tempUid', '". implode("','", array_map('trim', $otherUserIds)) ."')
AND
cr.user_id in ('$tempUid', '". implode("','", array_map('trim', $otherUserIds)) ."')
GROUP BY
cm.message_id
HAVING
countDistinct = ". count($otherUserIds) ."
AND
COUNT(DISTINCT cr.user_id) = ". count($otherUserIds) ."
ORDER BY
cm.chat_datetime DESC
LIMIT
$paginationConstant OFFSET $offsetVal

How to speed up this MySQL query? Latest messages query

i need speed up this query for list latest messages. This query running too long (eg. 10 seconds ...)
SELECT datas.uid,
datas.message,
datas.date,
CONCAT(conv.first_name, ' ', conv.last_name) AS conversation_name
FROM (SELECT m.message_id,
m.message,
IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.readed,
m.sended AS `date`
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
WHERE m.message_id IN (SELECT MAX(message_id)
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid),
GREATEST(from_uid, to_uid))) datas
LEFT JOIN users conv
ON conv.user_id = datas.uid
ORDER BY datas.date DESC
LIMIT 5
This query use 2 tables (users and messages).
Table users:
user_id (primary, autoincrement)
login
pass
first_name
last_name
....
Table messages:
message_id (primary, autoincrement)
from_uid (sender message, reference to table users -> user_id)
to_uid (receiver message, reference to table users -> user_id)
sended (timestamp)
message (varchar)
EDIT
I added indexes to messages:
- from_uid
- to_uid
- sended
and this is without efect...
Try creating indexed on the id's you're checking.
In this case you might want to create an index on: conv.user_id, datas.uid, m.message_id, messages.to_uid, messages.from_uid, and datas.date might be a good idea as well, since you're sorting on that.
Add indexes on from_uid and to_uid to speed up the SELECT MAX(message_id) subquery. Otherwise, it has to do a full scan of the table.
I would try and remove the sub query(s)
You can clean it up a bit by joining against users twice when getting the user details from te from and to uid. This way the joins can use indexes effectively. Then just use IF in the SELECT to decide which one to return:-
SELECT IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.message,
m.sended AS `date`,
IF (m.from_uid = 1, CONCAT(to_conv.first_name, ' ', to_conv.last_name), CONCAT(from_conv.first_name, ' ', from_conv.last_name)) AS conversation_name
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
LEFT JOIN users to_conv
ON to_conv.user_id = m.to_uid
LEFT JOIN users from_conv
ON from_conv.user_id = m.from_uid
WHERE m.message_id IN
(
SELECT MAX(message_id)
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid),
GREATEST(from_uid, to_uid)
)
ORDER BY date DESC
LIMIT 5
The sub query to check the message id is a bit more difficult to remove. Generally IN performs badly. Might be worth changing it to use EXISTS (although this will require the check in the HAVING clause which might not be good)
SELECT IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.message,
m.sended AS `date`,
IF (m.from_uid = 1, CONCAT(to_conv.first_name, ' ', to_conv.last_name), CONCAT(from_conv.first_name, ' ', from_conv.last_name)) AS conversation_name
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
LEFT JOIN users to_conv
ON to_conv.user_id = m.to_uid
LEFT JOIN users from_conv
ON from_conv.user_id = m.from_uid
WHERE EXISTS
(
SELECT MAX(message_id) AS max_message_id
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid), GREATEST(from_uid, to_uid)
HAVING m.message_id = max_message_id
)
ORDER BY date DESC
LIMIT 5

Mysql query ( LINK tickets, employee , cats , comments ) using JOIN

here is my tables
Tickets
tic_id,
tic_cat
tic_priority
tic_cus
tic_date
tic_title
tic_msg
tic_files
tic_emp
tic_moved
tic_statue
tic_rate
Employee
emp_id
emp_name
emp_username
emp_password
emp_cat
emp_special
emp_lastlogin
emp_session
emp_code
emp_statue
emp_master
emp_ip
Cats
cat_id
cat_type
cat_name
cat_statue
cat_delete
cat_date
cat_ip
cat_options
Comments
com_id
tic_id
cus_id
emp_id
com_msg
com_time
com_ip
com_statue
And I need the result as
tic_id | tic_cat | cat_name | tic_title | tic_statue | tic_priority | tic_msg | emp_name | comments_row | last_comment |
I Make this query but i have 2 problems
Query Is
SELECT
tickets.tic_id
,tickets.tic_cat
,cats.cat_name
,tickets.tic_title
,tic_statue
,tic_priority
,tickets.tic_msg
,employee.emp_name
,count(comments.com_id)
,( SELECT comments.com_msg
from comments
order by com_id DESC limit 1 )
AS last_comment
FROM tickets
LEFT JOIN employee
on (tickets.tic_emp = employee.emp_id)
LEFT join cats
on (tickets.tic_cat = cats.cat_id)
LEFT JOIN comments
on(tickets.tic_id = comments.tic_id)
WHERE tic_cus=2 /* 2 -> This Is Customer Id */
GROUP BY comments.tic_id
My Problems Is
i have 3 Result In Database To Customer Number 2 -> only show 2 results
i want to get Last Comment -> the 2 Result have the same last comment
How Can i do this Query With Out This 2 Errors
Edit The Post After New Query
Problem number Two Solved Using This Query
SELECT
tickets.tic_id
,tickets.tic_cat
,cats.cat_name
,tickets.tic_title
,tic_statue
,tic_priority
,tickets.tic_msg
,employee.emp_name
,count(comments.com_id)
,( SELECT comments.com_msg
from comments
WHERE tickets.tic_id = comments.tic_id
order by com_id DESC limit 1 )
AS last_comment
FROM tickets
LEFT JOIN employee
on (tickets.tic_emp = employee.emp_id)
LEFT join cats
on (tickets.tic_cat = cats.cat_id)
LEFT JOIN comments
on(tickets.tic_id = comments.tic_id)
WHERE tic_cus=2
GROUP BY comments.tic_id
Solved
SELECT
tickets.tic_id
,tickets.tic_cat
,cats.cat_name
,tickets.tic_title
,tic_statue
,tic_priority
,tickets.tic_msg
,employee.emp_name
,count(comments.com_id)
,( SELECT comments.com_msg
from comments
WHERE tickets.tic_id = comments.tic_id
order by com_id DESC limit 1 )
AS last_comment
FROM tickets
LEFT JOIN employee
on (tickets.tic_emp = employee.emp_id)
LEFT join cats
on (tickets.tic_cat = cats.cat_id)
LEFT JOIN comments
on(tickets.tic_id = comments.tic_id)
WHERE tic_cus=2
GROUP BY tickets.tic_id
1) Can you show some data in these tables to demonstrate? In other words, how do you know you should have three records? Does select * from tickets where tic_cus = 2 by itself return 3 records?
2) You need to filter the comments subquery by ticket. I would also recommend having the comment count in a subquery as well and leave out the GROUP BY altogether:
,( select count(comments.com_id)
from comments
where comments.tic_id = tickets.tic_id) as comment_count
,( select comments.com_msg
from comments
where comments.tic_id = tickets.tic_id
^^^^^^^ filter by ticket so not last of ALL comments
order by com_id DESC limit 1) as last_comment

Right mySql command to get meta information?

Wordpress is a good example of a web application that uses a table for user info, and then a meta lookup table for user data. The only problem is that the only way I know of to get a complete list of meta information for a list of users is to build the sql statement "manually" - either hard coded or with the help of PHP.
The user table looks something like this:
wp_users table
ID|user_login|user_email|user_pass|date_registered
==================================================
1| me |me#me1.com|f239j283r| 2011-01-01
wp_usermeta table
umeta_id|user_id|meta_key|meta_value
====================================
1 | 1 | phone | 123-4567
1 | 1 | fname | john
1 | 1 | lname | doe
I know I can do something like this (manually or with php) to achieve the result of what I want:
select *
from wp_users
left join wp_usermeta as phone on (ID = user_id) AND (meta_key = phone)
left join wp_usermeta as fname on (ID = user_id) AND (meta_key = fname)
left join wp_usermeta as lname on (ID = user_id) AND (meta_key = lname)
that yields something like this:
ID|user_login|user_email|user_pass|date_registered|phone |fname|lname
=================================================================+++===
1| me |me#me1.com|f239j283r| 2011-01-01 |123-4567|john |doe
I know mySql also has the GROUP_CONCAT thing, which is why I feel like there is a better way. That would look something like this:
select *, group_concat(meta_value) as all_meta
from wp_users
left join wp_usermeta on ID = user_id
group by wp_users.ID
So is there a way to get the result similar to that from the first sql statement with a more dynamic sql statement like the second one?
Edit
Doug has proposed an interesting solution, possibly using information_schema. I was having trouble getting that to work so I've posted a dump of the two tables for anyone who wants to test their SQL :) http://pastebin.com/w0jkxnws
Is this what you're looking for? It's still 3 statements, but, contrary to my previous statement to the contrary, there shouldn't be much prep cost.
set group_concat_max_len = 2048;
SELECT CONCAT('SELECT u.id, u.user_login, ', GROUP_CONCAT(concat('
(SELECT meta_value FROM wp_usermeta WHERE user_id = u.id AND meta_key = "', um.meta_key, '") `', um.meta_key, '`') SEPARATOR ", "), '
FROM wp_users u ') FROM (SELECT DISTINCT meta_key FROM wp_usermeta) um INTO #sql;
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
Usually this is done by running 2 queries against the database – first to fetch user record, second for properties.
Try this
select *
from wp_users
left join wp_usermeta as fname on (ID = user_id)
where meta_key in ('fname','lname','phone')
group by ID, meta_key;
You could try something like this:
SELECT
u.*,
MIN(CASE m.meta_key WHEN 'phone' THEN m.meta_value END) AS phone,
MIN(CASE m.meta_key WHEN 'fname' THEN m.meta_value END) AS fname,
MIN(CASE m.meta_key WHEN 'lname' THEN m.meta_value END) AS lname
FROM wp_users u
LEFT JOIN wp_usermeta m ON u.ID = m.user_id
AND m.meta_key IN ('phone', 'fname', 'lname')
GROUP BY u.ID

Categories