Here is how my database looks like:
table: conversations
+----+--------+--------+
| id | user_1 | user_2 |
+----+--------+--------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 1 | 3 |
+----+--------+--------+
table: messages
+----+--------------+------+
| id | conversation | text |
+----+--------------+------+
| 1 | 1 | hej |
| 2 | 1 | test |
| 3 | 2 | doh |
| 4 | 2 | hi |
| 5 | 3 | :) |
| 6 | 3 | :D |
+----+--------------+------+
Then when I run the followin query:
SELECT
*
FROM `messages`
INNER JOIN `conversations`
ON `conversations`.`id` = `messages`.`convesations`
GROUP BY `conversations`.`id`
ORDER BY `messages`.`id` DESC
Then I get those out from messages:
+----+--------------+------+
| id | conversation | text |
+----+--------------+------+
| 1 | 1 | hej |
| 3 | 2 | doh |
| 5 | 3 | :) |
+----+--------------+------+
But, is it somehow possible to do so that I will get the messages with the highest messages.id from that group, instead of the lowest?
EDIT: Here is the output I want from messages:
+----+--------------+------+
| id | conversation | text |
+----+--------------+------+
| 2 | 1 | test |
| 4 | 2 | hi |
| 6 | 3 | :D |
+----+--------------+------+
As those are the messages in same conversation with the highest id.
SELECT *
FROM conversations c
JOIN messages m
ON m.id =
(
SELECT id
FROM messages mi
WHERE mi.conversation = c.id
ORDER BY
mi.conversation DESC, mi.id DESC
LIMIT 1
)
Create an index on messages (conversation, id) for this to work fast.
You simply need to use nested query like this:
SELECT * FROM Messages
WHERE ID IN(
SELECT Max(m.ID) FROM Messages m
INNER JOIN conversations c
ON c.id = m.conversation
GROUP BY m.conversation
);
Output:
| ID | CONVERSATION | TEXT |
----------------------------
| 2 | 1 | test |
| 4 | 2 | hi |
| 6 | 3 | :D |
If you want data from both tables try this:
SELECT * FROM Messages m
JOIN conversations c
ON c.id = m.conversation
WHERE m.ID IN (
SELECT MAX(ID) FROM Messages
GROUP BY conversation
)
GROUP BY m.conversation;
Output:
| ID | CONVERSATION | TEXT | USER_1 | USER_2 |
----------------------------------------------
| 2 | 1 | test | 1 | 2 |
| 4 | 2 | hi | 2 | 3 |
| 6 | 3 | :D | 1 | 3 |
See this SQLFiddle
You are making your Join on the wrong column. 'Id' in Conversation cannot be equal to 'Id' in messages.
I thin, 'Conversation' in table messsages is 'id_conversation' right?
So, if I understood well :
SELECT *
FROM messages
INNER JOIN conversations
ON conversations.id = messages.conversation
GROUP BY conversations.id
ORDER BY messages.id DESC
I think you just have an incorrect table join:
SELECT *
FROM `messages`
INNER JOIN `conversations`
ON `conversations`.`id` = `messages`.`conversation`
GROUP BY `conversations`.`id`
ORDER BY `messages`.`id` DESC
Edit
You can try this:
SELECT *
FROM `messages`
WHERE `messages`.`id` IN (
SELECT MAX(id)
FROM messages
GROUP BY conversation
)
A couple of different approaches:
This approach relies on known but undocumented behaviour within MySQL, where the unaggregated, ungrouped values returned in a grouped query are the first in the sort order - it's fast, but should not be viewed as reliable:
SELECT * FROM
(SELECT * FROM messages
ORDER BY conversation, id desc) a
GROUP BY conversation
Alternatively, an approach that should always be reliable:
SELECT m.*, c.user_1, c.user_2 FROM messages m
JOIN (select conversation, max(id) max_id from messages group by conversation) l
ON m.id = l.max_id
JOIN conversations c
ON c.id = m.conversation
GROUP BY conversation
SQLFiddle here.
Related
Attempting to join two tables on user_id. The users table has unique id for each user. The user_codes table can have multiple rows with the same user_id. I only want to return 1 row from the joined user_codes table, where code_count is the largest.
users Table
| id | email |
| -------- | --------------- |
| 1 | user1#gmail.com |
| 2 | user2#gmail.com |
| 3 | user3#gmail.com |
user_code TABLE
| user_id | invite_code | count |
| -------- | ----------- | ------|
| 1 | X49MCL1 | 40 |
| 1 | K59CLT9 | 1000 |
| 2 | X5BC924 | 15 |
| 2 | 38DF80L | 8 |
| 3 | 641020T | 22 |
EXPECTED RESULT
| id | email | invite_code | count |
| --- | --------------- | ----------- | ------|
| 1 | user1#gmail.com | K59CLT9 | 1000 |
| 2 | user2#gmail.com | X5BC924 | 15 |
| 3 | user3#gmail.com | 641020T | 22 |
The query result only includes a single instance of each user found in the user_codes table with the highest count.
Here is the closest query I could get, but it only returns the invite_code and count for the first user.
SELECT a.id, a.email, b.invite_code, b.count
FROM users a
LEFT JOIN user_codes b
ON b.user_id = a.id
AND b.count = (SELECT MAX(count) FROM user_codes GROUP BY b.user_id)
The above query returns the result:
| id | email | invite_code | count |
| --- | --------------- | ----------- | ------ |
| 1 | user1#gmail.com | K59CLT9 | 1000 |
| 2 | user2#gmail.com | `NULL` | `NULL` |
| 3 | user3#gmail.com | `NULL` | `NULL` |
I can't seem to figure out how/why the records after the first one don't include the invite_code and the count.
Thanks for help!
On MySQL 8+, I suggest using the RANK() window function:
WITH cte AS (
SELECT u.id, u.email, uc.invite_code, uc.count,
RANK() OVER (PARTITION BY u.id ORDER BY uc.count DESC) rnk
FROM users u
INNER JOIN user_code uc
ON uc.user_id = u.id
)
SELECT id, email, invite_code, count
FROM cte
WHERE rnk = 1;
The RANK() function will also match multiple records per user tied for the highest count.
You might be able to salvage your current attempt by correlating the user inside the subquery to the outer query:
SELECT a.id, a.email, b.invite_code, b.count
FROM users a
LEFT JOIN user_codes b
ON b.user_id = a.id AND
b.count = (SELECT MAX(uc.count) FROM user_codes uc WHERE uc.user_id = a.id);
I'm struggling to write a query to get a thread that is related to only the specified users.
I've included some dummy data and my table schema below, as well as the expected output with some edge cases.
Any help is appreciated.
users
+----+--------+
| id | name |
+----+--------+
| 1 | User 1 |
| 2 | User 2 |
| 3 | User 3 |
+----+--------+
threads
+------+----------+
| id | title |
+------+----------+
| 1 | Thread 1 |
| 2 | Thread 2 |
| 3 | Thread 3 |
+------+----------+
thread_user
+-----------+---------+
| thread_id | user_id |
+-----------+---------+
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
+-----------+---------+
Pseudo Query
select all from threads where related user id's are exactly 1 and 2
Expected Output
+------+----------+
| id | title |
+------+----------+
| 2 | Thread 2 |
+------+----------+
Thread 2 is in the list of results because it's related to only User 1 and User 2.
Thread 1 is not in the list of results because it is only related to User 1 and not User 2.
Thread 3 is not in the list of results because it is related to User 1 and User 2 and User 3.
Edit to handle case 3 as well
select t.id, t.title
from users u
inner join thread_user tu on tu.user_id = u.id
inner join threads t on t.id = tu.thread_id
inner join (select thread_id
from thread_user
group by thread_id
having count(*) = 2) sq on sq.thread_id = t.id
where u.id in (1,2)
group by t.id, t.title
having count(*) = 2
I am not at my dev environment, so can't check for syntax correctness, but something like this should work,
select t.id, t.title
from users u
inner join thread_user tu on tu.user_id = u.id
inner join threads t on t.id = tu.thread_id
where u.id in (1,2)
group by t.id, t.title
having count(*) = 2
I have 3 tables:
Users
| id | name |
|----|-------|
| 1 | One |
| 2 | Two |
| 3 | Three |
Likes
| id | user_id | like |
|----|---------|-------|
| 1 | 1 | 3 |
| 2 | 1 | 5 |
| 3 | 2 | 1 |
| 4 | 3 | 2 |
Transations
| id | user_id | transaction |
|----|---------|-------------|
| 1 | 1 | -1 |
| 2 | 2 | 5 |
| 3 | 2 | -1 |
| 4 | 3 | 10 |
I need get sum of likes.like and transations.transation for each user and then sort it by its result.
I was able to do it for users and likes:
select users.*, sum(likes.like) as points
from `users`
inner join `likes` on `likes`.`user_id` = `users`.`id`
group by `users`.`id`
order by points desc
But then I add transactions table like this:
select users.*, (sum(likes.like)+sum(transactions.`transaction`)) as points
from `users`
inner join `likes` on `likes`.`user_id` = `users`.`id`
inner join `transactions` on `transactions`.`user_id` = `users`.`id`
group by `users`.`id`
order by points desc
It is show wrong results.
I expecting to see:
| id | name | points |
|----|-------|--------|
| 3 | Three | 12 |
| 1 | One | 7 |
| 2 | Two | 5 |
But get this instead:
| id | name | points |
|----|-------|--------|
| 3 | Three | 12 |
| 1 | One | 6 |
| 2 | Two | 5 |
So, how sort users by sum likes.like and transations.transation?
Thank you!
Since there's not a 1-to-1 relationships between transactions and likes, I think you need to use subqueries:
select users.*,
(select sum(points) from likes where user_id = users.id) as points,
(select sum(transaction) from transactions where user_id = users.id) as transactions
from users
order by points desc
Updated after more explanation of requirements:
select users.*,
(select sum(points) from likes where user_id = users.id) +
(select sum(transaction) from transactions where user_id = users.id) as points
from users
order by points desc
This seemed pretty simple to start with, but it's getting awkward.
Suppose we have a table containing...
+---------+-----------+
| chat_id | friend_id |
+---------+-----------+
| A | 1 |
| A | 2 |
| A | 3 |
| B | 1 |
| B | 2 |
| C | 1 |
| C | 2 |
| C | 3 |
| D | 1 |
| D | 2 |
| D | 3 |
| D | 4 |
| D | 5 |
| E | 0 |
| E | 1 |
| E | 2 |
| E | 3 |
| E | 4 |
| E | 5 |
| E | 6 |
| E | 7 |
| F | 0 |
| F | 1 |
| G | 1 |
| G | 2 |
+---------+-----------+
And I wish to select only those chat_id that have friend_ids 1 and 2 and no other friend_id, what would the SQL be to get B and G returned?
So far, the best I've come up with is:
SELECT DISTINCT a.chat_id, COUNT(*)
FROM tt2 a
LEFT JOIN tt2 b
ON a.chat_id = b.chat_id
AND b.friend_id NOT IN (1,2)
WHERE a.friend_id in (1,2)
and b.chat_id IS NULL GROUP BY a.chat_id HAVING COUNT(*) = 2;
+---------+----------+
| chat_id | count(*) |
+---------+----------+
| B | 2 |
| G | 2 |
+---------+----------+
2 rows in set (0.00 sec)
And just in case I was looking for chat_id where only 1,2,3 exist...
SELECT DISTINCT a.chat_id, COUNT(*)
FROM tt2 a
LEFT JOIN tt2 b
ON a.chat_id = b.chat_id
AND b.friend_id not in (1,2,3)
WHERE a.friend_id IN (1,2,3)
AND b.chat_id IS NULL
GROUP BY a.chat_id
HAVING COUNT (*) = 3;
+---------+----------+
| chat_id | count(*) |
+---------+----------+
| A | 3 |
| C | 3 |
+---------+----------+
But this table could get massive and I need the SQL to be swift, does anyone know a better way?
To try and clarify... I get given a bunch of friend_id's and I want to get chat_id where only those friend_id exist for that chat_id.... with the SQL being quick (on sqlite)
Many thanks in advance!
Here's an option that should be able to limit the amount of data needed
SELECT
d.chat_id,
COUNT(DISTINCT s.friend_id) AS matchedFriends,
COUNT(DISTINCT d.friend_id) AS totalFriends
FROM tt2 AS d
INNER JOIN tt2 AS s
ON s.chat_id = d.chat_id
AND s.friend_id IN (1,2)
GROUP BY d.chat_id
HAVING matchedFriends = 2
AND totalFriends = matchedFriends
The INNER JOIN s makes sure that it only hits rows that have got at least one of the requested friends in. The matchedFriends count checks how many of the requested friends are found.
The totalFriends count then checks how many friends in total are on that chat.
Finally the HAVING first makes sure there are 2 matched friends, and then checks the number of friends in total equals the number of matched friends.
This will require you to supply both a list of friends, and a number of friends you are looking for, but should be efficient.
For increased efficiency, have an index on (chat_id,friend_id) (if you don't already, assuming it's a 2-part PK at time of writing)
Try this:
SELECT chat_id, GROUP_CONCAT(DISTINCT friend_id ORDER BY friend_id) AS friends
FROM table_1
GROUP BY chat_id
HAVING friends = '1,2'
Note: This works in mysql but I doubt that it will work on sqlite.
Hey again, im still getting a hang of Queries and stuff so please excuse all the frequent sql questions 8)
Anyways im trying to select rows after a certain value.
Dont understand? Ok this is how it actually looks.
Table: messages
+------------+-----------+---------+-------+---------------------------------------+----------------+------------------+
| message_id | thread_id | user_id | to_id | body | message_status | uid_sent_deleted |
+------------+-----------+---------+-------+---------------------------------------+----------------+------------------+
| 1 | 1 | 1 | 7 | How are you bro? | read | 1 |
| 2 | 1 | 7 | 1 | IM good what about you kenny? | read | 0 |
| 3 | 1 | 1 | 7 | Same just chilling how is your sister | read | 1 |
| 4 | 1 | 7 | 1 | Shes coool u know just doin great | read | 0 |
| 7 | 1 | 1 | 7 | Thats nice | read | 1 |
| 8 | 1 | 7 | 1 | Yupp | read | 0 |
| 9 | 1 | 1 | 7 | hhahaha | read | 1 |
| 10 | 1 | 7 | 1 | anyways.... | unread | 0 |
+------------+-----------+---------+-------+---------------------------------------+----------------+------------------+
Where the message id is 9, is has the value of '1' in the column uid_sent_deleted.
Im trying to select rows after the last row that has a value of '1' in the column uid_sent_deleted.
here is the sql that im currently working with. Thanks for the help!!
SELECT message_id, thread_id, messages.user_id, to_id, body, message_status, uid_sent_deleted
FROM messages
INNER JOIN messages_thread ON messages_thread.id = messages.thread_id
WHERE thread_id =1
GROUP BY messages.message_id
ORDER BY message_id ASC
... WHERE thread_id = 1 AND id >
(SELECT MAX(id) FROM messages WHERE uid_sent_deleted = 1)
SELECT message_id, thread_id, messages.user_id, to_id, body, message_status, uid_sent_deleted
FROM
(SELECT thread_id t_id, MAX(message_id) m_id
FROM messages
WHERE thread_id =1 and uid_sent_deleted = 1
GROUP BY thread_id) n
inner join messages on messages.message_id > n.m_id and messages.thread_id = n.t_id
INNER JOIN messages_thread ON messages_thread.id = messages.thread_id
ORDER BY message_id ASC
LIMIT 1
A subquery might work, you select the MAX(message_id) WHERE uid_sent_deleted=1, then do you join ... little hazy on the syntax.