I need to select all chats and right now I'm selecting them using multiple queries like this:
$query_1 = mysqli_query($database, "SELECT chat_id, user_id_1, user_id_2
FROM chat
WHERE user_id_1 = '$user_id' OR user_id_2 = '$user_id')");
if (mysqli_num_rows($query_1) > 0) {
while ($row = mysqli_fetch_assoc($query_1)) {
$chat_id = $row['chat_id'];
$user_id_1 = $row['user_id_1'];
$user_id_2 = $row['user_id_2'];
if ($user_id_1 == $user_id)
$user_id_chat = $user_id_2;
else
$user_id_chat = $user_id_1;
$query_2 = mysqli_query($database, "SELECT username
FROM user
WHERE user_id = '$user_id_chat'");
$row2 = mysqli_fetch_assoc($query_2);
$username = $row2['username'];
$query_3 = mysqli_query($database, "SELECT text,
(SELECT COUNT(message_id) FROM message WHERE chat_id = '$chat_id' AND user_id != '$user_id' AND seen = '0') AS unread_messages
FROM message
WHERE chat_id = '$chat_id'
ORDER BY message_id DESC
LIMIT 1");
$row3 = mysqli_fetch_assoc($query_3);
$text = $row3['text'];
$unread_messages = $row3['unread_messages'];
}
}
Is there a way to get all this ($chat_id, $user_id_chat, $username, $text and $unread_messages) using only one query?
EDIT: Added example data and expected output:
Table user
user_id username ...
1 User1 ...
2 User2 ...
3 User3 ...
4 User4 ...
Table chat
chat_id user_id_1 user_id_2 ...
1 3 4 ...
2 1 3 ...
Table message
message_id chat_id user_id text seen ...
1 1 4 Hi! 1 ...
2 2 1 Hello 1 ...
3 1 3 hi 0 ...
4 2 3 Hi 0 ...
5 2 3 How are you? 0 ...
If $user_id is 1 there should be only one chat and value of $chat_id should be 2, of $user_id_chat should be 3, of $username should be 'User3', of $text should be 'How are you?' and of $unread_messages should be 2.
http://sqlfiddle.com/#!9/8c6cb/8
$query_1 = mysqli_query($database,
"SELECT
c.chat_id,
u.username,
COALESCE(m.text,'MY PLACEHOLDER'),
COALESCE(um.unread_messages,0)
FROM (
SELECT chat_id,
IF(user_id_1 = '$user_id', user_id_2, user_id_1) user_id_chat
FROM chat
WHERE user_id_1 = '$user_id' OR user_id_2 = '$user_id'
) c
LEFT JOIN user u
ON u.user_id = c.user_id_chat
LEFT JOIN (SELECT chat_id, SUM(seen = '0',1,0) unread_messages, MAX(message_id) max_id
FROM message
WHERE user_id != '$user_id'
GROUP BY chat_id) um
ON um.chat_id = c.chat_id
LEFT JOIN message m
ON m.chat_id = c.chat_id
AND m.message_id = um.max_id ");
if (mysqli_num_rows($query_1) > 0) {
while ($row = mysqli_fetch_assoc($query_1)) {
$chat_id = $row['chat_id'];
$username = $row['username'];
$text = $row ['text'];
$unread_messages = $row['unread_messages'];
}
}
So, you are looking for a way to select from two tables in one query. Then you might like INNER JOIN.
Visit this link:
http://www.w3schools.com/sql/sql_join_inner.asp
OR
Make a function to select like this:
` function select ($query ="SELECT * FROM $table", $limit = 1) {
$row = mysql_query($query);
// I'm so lazy to type the other codes, just put ur while loop and other stuff(if any) here
} `
This then must be called as:
` select("SELECT chat_id, user_id_1, user_id_2
FROM chat
WHERE user_id_1 = '$user_id' OR user_id_2 = '$user_id')");
select(" (SELECT COUNT(message_id) FROM message WHERE chat_id = '$chat_id' AND user_id != '$user_id' AND seen = '0') AS unread_messages
FROM message
WHERE chat_id = '$chat_id' AND user_id != '$user_id'
ORDER BY message_id DESC
LIMIT 1"); `
This won't need multiple where loops.
Sorry for bad code layout, I'm a newbie here.
Good Luck!
Related
How to write following query in CakePHP 3. It has "having" clause in sub-query
the following query is working but what is the way to write CakePHP query for following complex query.
$query = "SELECT users.name, users.Id ,users.primary_image ,genders.name as genderName, FLOOR(DATEDIFF(CURDATE(),users.DOB) / 365) AS age, countries.Name as countryName, states.Name as stateName,
user_log.online_status
FROM users_address, genders , countries, states, user_log , users
WHERE gender_id = " . $looksFor->gender_id ."
AND Users_address.user_id = users.Id
AND genders.Id = users.gender_id
AND Users_address.country_id = countries.Id
AND Users_address.state_id = states.Id
AND user_log.user_id = users.Id
AND users.Id IN (
SELECT users_responses.user_id
FROM users_responses
WHERE". $conditions_list ."
GROUP BY user_id
HAVING COUNT(DISTINCT question_id, response_id) > ".$thirty_percent." );
ORDER BY RAND()
LIMIT 15 ";
}
$thirty_percent and $conditions_list have the following data which is used in sub-query to find out the results.
$thirty_percent = (int)($conditions_count/3.3 );
$conditions_list = (question_id = 3 AND response_id = 6)OR
(question_id = 5 AND response_id = 8)OR
(question_id = 12 AND response_id = 33)OR
(question_id = 6 AND response_id = 17)OR
(question_id = 9 AND response_id = 26)OR
(question_id = 7 AND response_id = 18)OR
(question_id = 1 AND response_id = 5)OR
(question_id = 8 AND response_id = 22)OR
(question_id = 2 AND response_id = 3)
EDIT:
I learnt first write sub query and then put that query in main query
Main Query: $this->Users->find('all',[
'contain'=>['Genders','Images','Users_Address', 'Countries','States'],
'conditions'=>[ 'user_id IN' => $Subquery ]
]);
Sub Query :
$Subquery = $UsersResponses->find('all',[
'conotain'=>[],
]])->where([$conditions_list, **here problem 1** ])->group('user_id')->having COUNT("(DISTINCT question_id, response_id)=>" = ".$thirty_percent." )here problem 2 ;
SQL sub-Query
SELECT users_responses.user_id
FROM users_responses
WHERE". $conditions_list ."
GROUP BY user_id
HAVING COUNT(DISTINCT question_id, response_id) > ".$thirty_percent." );
ORDER BY RAND()
LIMIT 15 ";
As you see in sub-query I need to put $conditions_list which have arrays of conditions and how we can put here in CAKEPHP.
problem 2 :need to use
GROUP BY user_id
HAVING COUNT(DISTINCT question_id, response_id) > ".$thirty_percent." );
ORDER BY RAND()
LIMIT 15 "; in sub-query but there is no clue how to do it.
I'm trying to return the row with ID 258776, here's what I've tried so far...
Here's my example, I use the AND msg.uid to only get one thread of messages. I want to return the latest post of each thread therefore in the example with just 2 messages for 1 thread, I want the row with ID 258776 to be returned.
Without GROUP BY:
SELECT main.id, main.message_id, main.inbox_id, main.uid, main.body, main.created_at FROM (
SELECT i.id as inbox_id, i.message_id, msg.* FROM inbox AS i
INNER JOIN message AS msg ON i.message_id = msg.id
WHERE i.profile_id = 2135
AND i.is_sent = 0
AND i.is_deleted = 0
#AND msg.uid = '570cc3a568402'
ORDER BY msg.updated_at DESC
) AS main #GROUP BY main.uid
= ID = =MSG = =INB = = UID = = BD = = CREATED =
258776 258776 524785 570cc3a568402 wtf 2016-06-22 11:34:29
217149 217149 438907 570cc3a568402 <br /> 2016-04-12 11:45:09
Try with GROUP BY
SELECT main.id, main.message_id, main.inbox_id, main.uid, main.body, main.created_at FROM (
SELECT i.id as inbox_id, i.message_id, msg.* FROM inbox AS i
INNER JOIN message AS msg ON i.message_id = msg.id
WHERE i.profile_id = 2135
AND i.is_sent = 0
AND i.is_deleted = 0
#AND msg.uid = '570cc3a568402'
ORDER BY msg.updated_at DESC
) AS main GROUP BY main.uid
= ID = =MSG = =INB = = UID = = BD = = CREATED =
217149 217149 438907 570cc3a568402 <br /> 2016-04-12 11:45:09
Switch to ASC but GROUP BY gives same results ?
SELECT main.id, main.message_id, main.inbox_id, main.uid, main.body, main.created_at FROM (
SELECT i.id as inbox_id, i.message_id, msg.* FROM inbox AS i
INNER JOIN message AS msg ON i.message_id = msg.id
WHERE i.profile_id = 2135
AND i.is_sent = 0
AND i.is_deleted = 0
#AND msg.uid = '570cc3a568402'
ORDER BY msg.updated_at ASC
) AS main GROUP BY main.uid
= ID = =MSG = =INB = = UID = = BD = = CREATED =
217149 217149 438907 570cc3a568402 <br /> 2016-04-12 11:45:09
I presume it should work fine if I didn't need to use an INNER join ? :(
EDIT:
Here's some more data from the message table and inbox table.
message
= ID = =FROMID= = UID = = TITLE = = BODY = = CREATED =
258776 52169 570cc3a568402 RE: RE: wtf 2016-06-22 11:34:29
258775 2135 570cc3a568402 RE: You Testtest 2016-06-22 11:31:29
258774 34833 576a590fdf9e5 RE: Sure < 3 < 3 2016-06-22 11:24:08
258773 34833 576a590fdf9e5 RE: Sure sok 2016-06-22 11:23:57
258772 34833 576a590fdf9e5 RE: Sure hey hey 2016-06-22 11:23:46
inbox
= ID = = PROFILE = = MSG_ID = = IS_SENT = = IS_READ = = IS_DELETED =
524785 2135 258776 0 0 0
524784 52169 258776 1 1 0
524783 52169 258775 0 1 0
524782 2135 258775 1 0 0
524781 2135 258774 0 1 0
If you want the max id that match you condition you should use
SELECT main.id, main.message_id, main.inbox_id, main.uid, main.body, main.created_at
FROM (
SELECT i.id as inbox_id, i.message_id, msg.* FROM inbox AS i
INNER JOIN message AS msg ON i.message_id = msg.id
WHERE i.id = (
SELECT max(i.id) FROM inbox AS i
INNER JOIN message AS msg ON i.message_id = msg.id
WHERE i.profile_id = 2135
AND i.is_sent = 0
AND i.is_deleted = 0
AND msg.uid = '570cc3a568402'
)
) AS main
Group by like said in comment are for aggegatte function (with multiplr result) you don't have this then you don't need
I ended up using PHP to solve my problem, by performing a PDO query to select all the messages, then loop through these results to have a simple array of message IDs grouped by the thread ID (uid)...
$pdo = Doctrine_Manager::connection();
$results = $pdo->execute('SELECT i.id, i.message_id, msg.uid FROM inbox AS i
INNER JOIN message AS msg ON i.message_id = msg.id
WHERE i.profile_id = :profile_id
AND i.is_sent = :is_sent
AND i.is_deleted = :is_deleted
ORDER BY msg.created_at ASC', [
':profile_id' => $address->getId(),
':is_sent' => 0,
':is_deleted' => 0,
])->fetchAll();
$whereIn = [];
foreach ($results as $key => $value) {
$whereIn[$value['uid']] = $value['id'];
}
return $this
->getBaseMessageBoxQuery()
->addWhere('i.profile_id = ?', $address->getId())
->andWhere('i.is_sent = 0')
->andWhere('i.is_deleted = 0')
->andWhereIn('i.id', array_values($whereIn))
->addGroupBy('msg.uid')
->orderBy('msg.created_at DESC');
To reiterate my original comment; unless there is some unusual logic you are trying to implement, I really don't see a need for either the GROUP BY or the subquery; just use LIMIT:
SELECT msg.id, i.message_id, i.id as inbox_id, msg.uid, msg.body, msg.created_at
FROM inbox AS i
INNER JOIN message AS msg ON i.message_id = msg.id
WHERE i.profile_id = 2135
AND i.is_sent = 0
AND i.is_deleted = 0
AND msg.uid = '570cc3a568402'
ORDER BY msg.updated_at DESC
LIMIT 1
EDIT: With the additional information, this should allow you to get what you want with just a query:
SELECT m.id, i.message_id, i.id as inbox_id, m.uid, m.body, m.created_at
FROM
(SELECT uid, MAX(updated_at) AS updated_at
FROM message
GROUP BY uid) AS lastMs
INNER JOIN message AS m USING(uid, updated_at)
INNER JOIN inbox AS i ON m.id = i.message_id
The primary concern I can see with this query, or any possible solution, with the problem as it has been described so far is that it is possible there may be multiple messages with the same uid AND updated_at; in which case, multiple results would be returned for that uid.
I am trying to write a long tail where statement which containing another select query and how I can do it. Currently, I am using Ignited Datatables Library with my CodeIgniter 3 installation.
SELECT ContactId,
ReceiverId,
SenderId,
IF(ReceiverId = 1,
(SELECT first_name
FROM users
WHERE users.id = contacts_connectivity.SenderId),
(SELECT first_name
FROM users
WHERE users.id = contacts_connectivity.ReceiverId)) AS FirstName,
IF(ReceiverId = 1,
(SELECT email
FROM users
WHERE users.id = contacts_connectivity.SenderId),
(SELECT email
FROM users
WHERE users.id = contacts_connectivity.ReceiverId)) AS SenderEmail,
IF(ReceiverId = 1,
(SELECT phone
FROM users
WHERE users.id = contacts_connectivity.SenderId),
(SELECT phone
FROM users
WHERE users.id = contacts_connectivity.ReceiverId)) AS SenderPhone,
IF(ReceiverId = 1,
(SELECT company
FROM users
WHERE users.id = contacts_connectivity.SenderId),
(SELECT company
FROM users
WHERE users.id = contacts_connectivity.ReceiverId)) AS SenderCompany,
ModulesId,
(SELECT ModuleName
FROM modules
WHERE ModuleId = contacts_connectivity.ModulesId) AS ModuledName,
FROM_UNIXTIME(AddedDate, "%m/%d/%Y") AddedDate
FROM contacts_connectivity
WHERE (ReceiverId = 1
OR SenderId = 1)
AND (ReceiverId
OR SenderId NOT IN
(SELECT (ReceiverId
OR SenderId)
FROM contacts_connectivity
WHERE (ReceiverId
OR SenderId) = 1))
I noticed that My query doesn't return any array at all. I already tried to write the where statement like this-
$this->datatables->where("(ReceiverId = ' . $id . ' OR SenderId = ' . $id . ')");
$this->datatables->where(" AND (ReceiverId OR SenderId NOT IN (SELECT (ReceiverId OR SenderId) FROM contacts_connectivity WHERE (ReceiverId OR SenderId) = ' . $id . '");
What am I doing wrong?
You invalid use parameters. Try something like this:
$this->datatables->where("(ReceiverId=$id OR SenderId=$id )");
Second string should be fixed too the same way. Also check that your $id param properly escaped.
Here is my query:
SELECT *
FROM messages
WHERE status = 1
AND (
poster IN (SELECT thing FROM follows WHERE follower = :uid AND type = 3)
OR
topic_id IN (SELECT thing FROM follows WHERE follower = :uid AND type = 1)
)
ORDER BY post_date DESC LIMIT 0, 20
I want to know which clause the rows come from. From the poster IN (...) part or the topic_id IN (...) part? How can I do that?
A straightforward way:
SELECT *
, CASE WHEN poster IN (SELECT thing FROM follows WHERE follower = :uid AND type = 3) THEN 'poster'
ELSE 'topic_id' END AS from_clause
FROM messages <..>
Another way :
SELECT m.*
, CASE WHEN t1.thing IS NULL THEN 'topic_id' ELSE `poster` END AS from_clause
FROM messages m
LEFT JOIN (SELECT thing FROM follows WHERE follower = :uid AND type = 3) t1 ON m.poster = t1.thing
LEFT JOIN (SELECT thing FROM follows WHERE follower = :uid AND type = 1) t2 ON m.topic_id = t2.thing
WHERE m.status = 1 AND (t1.thing IS NOT NULL OR t2.thing IS NOT NULL)
I have two tables, messages and thread messages is the main table and thread is where I keep the conversation between two users. I want to select only from messages table and order by thread_id from thread table. I am using the below sql but it doesn't order as I want it.
My aim is when new message is inserted to thread table make the parent message in messages table appear at the top. Does anyone have any idea?
SELECT m.*
FROM messages AS m
JOIN thread AS t
ON t.thread_id = m.id
WHERE ( m.to_user = ?
AND m.deleted != ?
AND m.del2 != ?)
OR
(m.from_user = ?
AND m.conversation = 'true'
AND m.deleted != ?
AND m.del2 != ?)
ORDER BY t.thread_id desc);
messages table
id to_user from_user message is_read deleted del2 conversation
----------------------------------------------------------------------------------------
1 user1 user2 hi mark, true true
2 user3 user4 wass up yo? true true
3 user1 user3 blah blah
thread table
thread_id thread_to_user thread_from_user thread_message thread_message_id thread_is_read
---------------------------------------------------------------------------------------------------
1 user2 user1 hi there, 1 1
2 user1 user2 hey, wassup 1 1
3 user2 user1 not much, hw u doin 1 1
4 user1 user2 doing great and you? 1 0
5 user3 user4 heyyyy 2 1
6 user4 user3 hi, u coming? 2 0
What I am expecting when echoing the row:
id to_user from_user message is_read deleted del2 conversation
----------------------------------------------------------------------------------------
2 user3 user4 wass up yo? true true
1 user1 user2 hi mark, true true
3 user1 user3 blah blah
Try this one:
SELECT DISTINCT(id), to_user,from_user,
message, is_read, conversation
FROM messages
LEFT JOIN (
SELECT thread_id, thread_message_id
FROM messages m
LEFT JOIN thread t
ON m.id = t.thread_message_id
) tbl
ON messages.id = tbl.thread_message_id
ORDER BY tbl.thread_id DESC
See fiddle demo
You could put your WHERE inside the subquery, like:
SELECT DISTINCT(id), to_user,from_user,
message, is_read, conversation
FROM messages
LEFT JOIN (
SELECT thread_id, thread_message_id
FROM messages m
LEFT JOIN thread t
ON m.id = t.thread_message_id
WHERE ( m.to_user = ?
AND m.deleted != ?
AND m.del2 != ?)
OR
(m.from_user = ?
AND m.conversation = true
AND m.deleted != ?
AND m.del2 != ?)
) tbl
ON messages.id = tbl.thread_message_id
ORDER BY tbl.thread_id DESC
See Fiddle Demo with WHERE
Update (Using MAX() with subquery)
SELECT id, to_user, from_user, message, is_read, conversation
FROM (
SELECT m.id, max(t.thread_id) thread_id, m.to_user,m.from_user,m.message, m.is_read, m.conversation
FROM messages m
LEFT JOIN thread t on(t.thread_message_id = m.id)
WHERE ( m.to_user = 'user1'
AND m.deleted != true
AND m.del2 != true)
OR
(m.from_user = 'user3'
AND m.conversation = true
AND m.deleted != true
AND m.del2 != true)
GROUP BY m.id
ORDER BY thread_id desc
) tbl
See Other Demo
please refer the below query and the fiddle example
select m.id, max(t.thread_id) thread_id, m.to_user,m.from_user,m.message, m.is_read, m.conversation
from messages m
left join thread t on(t.thread_message_id = m.id)
group by t.thread_message_id
order by thread_id desc;
fiddle example