sql messages table query - mysql

this is my conversation table:
conversationID || userID
1 || 1
1 || 2
2 || 1
2 || 2
2 || 3
as you can see each conversation can contain 2 users or more.
I am trying to get the id of the conversation that only 2 users are there.
ie conversation that contains only users 1 & 2, the answer is conversation 1.
but how do I get it?

This will select all conversations that have users 1 or user 2, or both, but no one else:
select conversationID
from conversations
group by conversationID
having count(*) = count(case when userID in (1,2) then 1 end)
If you also want all conversations that have exactly user 1 and 2, and no one else, you also have to add an and condition:
select conversationID
from conversations
group by conversationID
having count(*) = count(case when userID in (1,2) then 1 end)
and count(*) = 2 -- number of elements in set
If userID can be duplicated, it's also better to use distinct:
select conversationID
from conversations
group by conversationID
having
count(distinct userID) = count(distinct case when userID in (1,2) then userID end)
and count(distinct userID) = 2 -- number of elements in set

You should use having clause. Assuming that ( conversationID, userID ) is PK or AK, your query is:
select conversationID
from your_Table
group by conversationID
having count( * ) = 2
Edited Joined with 1,2 user conversations, this is the index friendly approach with out correlate subquery and without function by row.
select t1 conversationID
from your_Table t1
inner join
( select distinct conversationID
from your_Table
where userId in (1, 2)
) t2
on t1.conversationID = t2.conversationID
group by t1.conversationID
having count( distinct t1.userId ) = 2

Hope this helps you,
select conversationID from conversation
group by ConversationID having count(distinct UserID)=2;
sqlfiddle demo

Related

(My)SQL - For showing last Inbox of Messages

So hopefully the question is not asked, but I didn't find it.
My example table looks like this: https://imgur.com/a/BBykp
For my SQL I know for example the user ID of 1 and I want to show the Inbox of my Messanger for that I need to know if User ID 1 received a message or was the sender of a message.
For example:
sender_id receiver_id
1 2
3 1
1 4
12 1
Should give me four Inbox results for ID 1.
So I currently stuck with
SELECT * FROM chats AS tm
WHERE tm.id IN
(SELECT MAX(id) FROM chats WHERE sender_id = 1 OR receiver_id = 1 GROUP BY receiver_id)
This gets me:
https://imgur.com/a/P665T
You'll see that I don't need ID 2, but I care for ID 3, because it's the latest message of that conversation.
So how can I decide with SQL that I want the latest of sender_id 1 or receiver_id 1?
Thank you!
One option uses a least/greatest trick:
SELECT t1.*
FROM chats t1
INNER JOIN
(
SELECT
LEAST(sender_id, receiver_id) AS sender_id,
GREATEST(sender_id, receiver_id) AS receiver_id,
MAX(updated_at) AS max_updated_at
FROM chats
GROUP BY
LEAST(sender_id, receiver_id),
GREATEST(sender_id, receiver_id)
) t2
ON LEAST(t1.sender_id, t1.receiver_id) = t2.sender_id AND
GREATEST(t1.sender_id, t1.receiver_id) = t2.receiver_id AND
t1.updated_at = t2.max_updated_at;
Demo
SELECT * FROM chats AS tm WHERE tm.id IN (SELECT MAX(id) FROM chats
WHERE sender_id = 1 OR receiver_id = 1 GROUP BY receiver_id)
You could try with Top(1) like this
SELECT id FROM chats
WHERE sender_id = 1 OR receiver_id = 1 ORDER BY ID DESC
LIMIT 1
This is the last row in chats witch contains a sender or reciver with id 1
Is a first a aproximation (It´s suboptimal)

Group by select based on OR condition

After using UNION with two select queries, I'm getting following results
UserId Name Status
------ ------ --------
1 User1 Active
2 User2 Active
1 User1 InActive
3 User3 InActive
But the expected results is
UserId Name Status
---------------------
1 User1 Active
2 User2 Active
3 User3 InActive
Here what I need is, I want to group by column Id and get status as Active if any one result is active. How to form a SQL query for this?
Can anyone suggest query for any one of the following DB?
MSSQL
Oracle
MySQL
PostgreSQL
Edit:
This is the query I've tried in PostgreSQL
(SELECT DISTINCT User.Id,User.DisplayName,AppAccessToUsers.IsActive='1' AND User.IsActive='1' AS IsStatusActive
FROM Applications Left JOIN AppAccessToUsers ON (Applications.Id=AppAccessToUsers.ApplicationId)
Left JOIN User ON (AppAccessToUsers.UserId=User.Id) WHERE Applications.ClientId='e7e66c1b-b3b8-4ffb-844b-fc4840803265')
UNION
(SELECT DISTINCT User.Id,User.DisplayName,AppAccessToGroups.IsActive='1' AND Group.IsActive='1' AND UserGroup.IsActive='1' AND User.IsActive='1' AS IsStatusActive
FROM Applications Left JOIN AppAccessToGroups ON (Applications.Id=AppAccessToGroups.ApplicationId)
Left JOIN Group ON (AppAccessToGroups.GroupId=Group.Id) Left JOIN UserGroup ON (Group.Id=UserGroup.GroupId)
Left JOIN User ON (UserGroup.UserId=User.Id) WHERE Applications.ClientId='e7e66c1b-b3b8-4ffb-844b-fc4840803265')
Use this query,
SELECT UserId
,Name
,CASE WHEN min(status) = 'Active' THEN 'Active' ELSE 'InActive' END
FROM users GROUP BY UserId,Name
I would do the following, assuming a) your tables are called t1 and t2 (amend as appropriate for your actual table names) and b) the names for each userid in both tables are the same - ie. for userid = 1, both tables have the same name:
SELECT userid,
NAME,
MIN(status)
FROM (SELECT userid, NAME, status FROM t1
UNION ALL
SELECT userid, NAME, status FROM t2)
GROUP BY userid, NAME;
This works in Oracle, and I'm pretty sure it'll work in the other database platforms you mentioned.
N.B. I used MIN(status) since you appear to want a status of Active to override a status of Inactive, and A comes before I in the alphabet.
In Sql-server, you could use group by or Row_number like this
DECLARE #SampleData AS TABLE
(
UserId int,
Name varchar(20),
Status varchar(10)
)
INSERT INTO #SampleData
(
UserId,Name,Status
)
VALUES
(1,'User1', 'Active'),
(2,'User2', 'Active'),
(1,'User1', 'InActive'),
(3,'User3', 'InActive')
-- use row_number
;WITH temp AS
(
SELECT *, row_number() OVER(PARTITION BY sd.UserId ORDER BY sd.Status ) AS Rn
FROM #SampleData sd
)
SELECT t.UserId, t.Name, t.Status
FROM temp t WHERE t.Rn = 1
--or use group by
SELECT sd.UserId, sd.Name, min(sd.Status) AS status
FROM #SampleData sd
GROUP BY sd.UserId, sd.Name
Results:
UserId Name Status
1 User1 Active
2 User2 Active
3 User3 InActive
In case of MS Sql Server you can try row_number
;with cte as (
select top 1 with ties * from
( select * from #youruser
union all
select * from #youruser) a
order by row_number() over (partition by userid order by [status] desc)
) select * from cte where status = 'Active'
select your_table.* from your_table
inner join (
select UserId, min(Status) as st from your_table
group by UserId
) t
on your_table.UserId = t.UserId and your_table.Status = t.st
Note: if same UserId can have same Status more than 1 times, then this returns duplicated results.
;With cte (UserId, Name,Status)
AS
(
SELECT 1,'User1','Active' Union all
SELECT 2,'User2','Active' Union all
SELECT 1,'User1','InActive' Union all
SELECT 3,'User3','InActive'
)
SELECT UserId
,NAME
,[Status]
FROM (
SELECT *
,ROW_NUMBER() OVER (
PARTITION BY UserId
,NAME ORDER BY STATUS
) AS Seq
FROM cte
) dt
WHERE dt.Seq = 1
OutPut
UserId Name Status
-----------------------
1 User1 Active
2 User2 Active
3 User3 InActive
for postgres you can use CASE and bool_or, eg:
t=# with a(i,n,b) as (
values (1,'a','active'), (1,'a','inactive'), (2,'b','inactive'), (2,'b','inactive')
)
select i,n,case when bool_or(b = 'active') then 'active' else 'inactive' end
from a
group by i,n
;
i | n | case
---+---+----------
1 | a | active
2 | b | inactive
(2 rows)
Another approach:
Note : Group by is to remove duplicate
select
A.USERID, A.NAME,A.STATUS
from TAB_1 A
LEFT JOIN
(SELECT * FROM TAB_1 WHERE STATUS='Active') B
ON A.USERID=B.USERID
WHERE
( B.STATUS IS NULL OR A.STATUS=B.STATUS)
GROUP BY A.USERID, A.NAME,A.STATUS
ORDER BY A.USERID
;

MySQL check if comma separated values match rows in table

I'm trying to figure out how I can query my table to see if a group of user_id's match a conversation_id.
Query 1 should return result for:
user_id 1 is looking to see if there are any conversation_id's with just user_id = 2 and user_id = 1 in it. (Should return a row for each conversation_id = 1, 2, 4, 5 based on SQL Fiddle example)
conversation_id
1
2
4
5
Query 2 should return result for:
user_id 1 is looking to see if there are any conversation_id's with user_id = 2, user_id = 1, and user_id = 4 in it. (Should return 0 rows as it doesn't exist in the SQL Fiddle example)
The table setup is located at
SQL Fiddle
You can use a combination of group by ... having and a correlated exists subquery to achieve the result you want:
-- Query 1:
SELECT
conversation_id
FROM
users_conversations uc
where not exists (
select 1 from users_conversations
where conversation_id = uc.conversation_id
and user_id not in (1,2)
)
group by conversation_id
having count(distinct user_id) = 2;
-- Query 2: same query, only different numbers.
SELECT
conversation_id
FROM
users_conversations uc
where not exists (
select 1 from users_conversations
where conversation_id = uc.conversation_id
and user_id not in (1,2,4))
group by conversation_id
having count(distinct user_id) = 3;
Sample SQL Fiddle
Note that the first query will not return 1,2,4,5 but rather 2,5 but in your sample data neither 1 or 4 has only user_id 1 and 2 as participants (conversation 1 has 1,2,3,4, and conversation 4 has 1,2,5).
If i understand it right it should be something like his.
Q1:
SELECT
CASE
WHEN
count(distinct CASE WHEN user_id in ('1','2') THEN user_id END)>='2'
THEN `conversation_id`
END 'test'
FROM
users_conversations
where 1
group by `conversation_id`
Q2:
SELECT
CASE
WHEN
count(distinct CASE WHEN user_id in ('1','2','4') THEN user_id END)>='3'
THEN `conversation_id`
END 'test'
FROM
users_conversations
where 1
group by `conversation_id`
http://sqlfiddle.com/#!9/fb29d/9

SQL count of return users

I'm trying to return a count of return users which happens when there is a duplicate 'user_id and action_type'.
So if you refer below, I would like my output to be = 2, since user_id (5) has 2 similar action_types (234) and user_id (6) also has 2 similar action_types (585).
How do I structure my query to reflect this?
Table t1
User_Id Action_Type
--------- ------------
5 234
5 846
5 234
6 585
6 585
7 465
SELECT COUNT(DISTINCT User_Id) FROM (
SELECT User_Id
FROM t1
GROUP BY User_Id, Action_Type
HAVING COUNT(*) > 1
) t
SELECT COUNT(User_ID) DuplicateRecordsUsers
FROM
(SELECT User_ID, Action_Type, COUNT(User_ID) Records
FROM Table
GROUP BY User_ID, Action_Type
HAVING COUNT(User_ID) > 1
)
SELECT COUNT(User_Id) FROM (
SELECT User_Id
FROM t1
GROUP BY User_Id, Action_Type
HAVING COUNT(*) > 1
) t
DISTINCT not required just count the ids returned

How to select more rows from subquery

When I'm trying to run this query:
select * FROM `activity`
WHERE user_id = 1
AND activity_id NOT LIKE (select activity_id from activity where user_id = 1 ORDER BY activity_id DESC LIMIT 8)
I get the follow error:
Subquery returns more than 1 row
How can I solve this problem? I want to select the activity_id from the table excluding the latest 8 activity_id's for a certain user.
NOT LIKE is expecting an expression or a value to compare against and not a resultset.
Change NOT LIKE for NOT IN
Try this one:
SELECT * FROM `activity`
WHERE user_id = 1 AND activity_id NOT IN (
SELECT activity_id FROM activity WHERE user_id = 1
ORDER BY activity_id DESC LIMIT 8)
Solved it by doing this:
$sql2 = "DELETE t1.*
FROM activity t1
left join (select activity_id from activity where user_id = '".$row['user_id']."' ORDER BY activity_id DESC LIMIT 8) t2
on (t1.activity_id = t2.activity_id)
where t2.activity_id is null
and t1.user_id = '".$row['user_id']."'";