Check if a chat between 2 people exists - sql

I have a chat system that handles group chats. A conversation can have many participants and the only distinction between group and non group chats is a non group chat will only have 2 participants.
I want a query to check if a non group chat exists between 2 users.
Conversations table
| id |
Participants table
| id | conversation (FK) | user (FK to a users table) |
To check if a single conversation exists I have come up with this query
select participants.conversation, CAST ( count(participants.user) AS INTEGER )
as members
from participants
where participants.user = 3 or participants.user = 18
group by participants.conversation
having CAST ( count(participants.user) AS INTEGER ) = 2
I Have created a single chat between users 3 and 18, and also a group chat between 3, 18 and 17. But when I run this query, both conversations are retuning 2 members, when the group chat has 3 participants.
What am I doing wrong?

You can get all such non-groups with an aggregation query:
select conversation
from participants p
group by conversation
having count(*) = 2 and
count(*) filter (where p.user = 3) = 1 and
count(*) filter (where p.user = 18) = 1;
Or a simpler having clause:
having array_agg(p.user order by p.user) = array[3,18]

the main problem is that query filters all users (except 2) first, before grouping, this is why all conversations looks like 2-user groups, I suggest to rewrite query like this:
with chats as (
select distinct conversation
from participants
where "user" in (3, 18)
),
nongroup as (
select conversation
from participants
where conversation in (select conversation from chats)
group by conversation
having count(*) = 2
)
select * from nongroup
first CTE will pick all conversations for given 2 users, second CTE will pick only 2 user rooms
fiddle: http://sqlfiddle.com/#!15/b5e25/3

Related

In SQL, how can i segment users by number of items they have? (redshift)

I'm not a SQL expert so apologies if this is actually really simple.
I have a table that lists users and the different questionnaires they have taken. Users can take questionnaires in any order and take as many as they like. There are a total of 7 available and I want to get a view of how many have taken 1 out of 7, 2 of 7, 3 of 7 etc etc
So a really rough example is the table might look like this:
And I want a query that will show me:
count Users with 1 Q: 1
count Users with 2 Q: 2
count Users with 3 Q: 0
count Users with 4 Q: 0
count Users with 5 Q: 1
count Users with 6 Q: 0
count Users with 7 Q: 0
You can do this with two levels of aggregation:
select cnt_questionnaires, count(*) cnt_users
from (
select count(*) cnt_questionnaires from mytable group by userID
) t
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t ;
create table #t (userid INT, q nvarchar(32));
insert into #t
values
(1,'Q1'),
(1,'Q3'),
(2,'Q2'),
(3,'Q1'),
(3,'Q2'),
(3,'Q3'),
(3,'Q4'),
(3,'Q5'),
(4,'Q2'),
(4,'Q3')
-- select * from #t
SELECT
v.qCount,
Count(c.userid) uCount
FROM
(VALUES (1),(2),(3),(4),(5),(6),(7)) v(qCount)
LEFT JOIN (
select
userid, count(q) qCount
from
#t
group by userid
) c ON c.qCount = v.qCount
GROUP BY
v.qCount
Assuming you have user_id on each row, the challenge is getting the zero values. Redshift is not very flexible when it comes to creating tables. Assuming your source data has enough rows, you can use:
select n.n, coalesce(u.cnt, 0)
from (select row_number() over () as n
from t
limit 7
) n left join
(select user_id, count(*) as cnt
from t
group by user_id
) u
on n.n = u.cnt;

my query returns many records per id, I just one any of them per id

Hi friends from Stack Overflow. I am trying to run a query in MS Access, i just want one record per ID randomly, but I am getting all.
I tried with distinct is not working, and I try with top, didn't work either.
this is my table below originaldata
ID HotelName Role Email
__________________________
1 bandb admin test1#email.com
1 bandb admin test2#email.com
1 bandb admin test3#email.com
1 bandb user myuser#email.com
2 myhtl admin myhotel#email.com
3 ben admin ben#test.com
3 ben user ben2#test.com
4 moon admin moon#moon.com
4 moon admin moon#moon2.com
I want to get the below results
ID HotelName Role Email
__________________________
1 bandb admin test1#email.com
2 myhtl admin myhotel#email.com
3 ben admin ben#test.com
4 moon admin moon#moon.com
SELECT *
FROM OriginalData
WHERE (((OriginalData.[Role])='admin') AND ((OriginalData.[ID]) In (Select Distinct [ID] from [OriginalData] where [Role] = 'Admin' )));
Thank you for your time and help
SELECT ID, min(HOTELNAME), min(ROLE), min(EMAIL)
from OriginalData
group by ID
Assuming you would be OK with displaying the "minimum" email address per each hotel group, then the following query should work:
SELECT od1.ID, od1.HotelName, od1.Role, od1.Email
FROM OriginalData od1
INNER JOIN
(
SELECT ID, Hotel, MIN(Email) AS min_email
FROM OriginalData
WHERE Role = 'admin'
GROUP BY ID, Hotel
) od2
ON od1.ID = od2.ID AND
od1.Hotel = od2.Hotel AND
od1.Email = od2.min_email
WHERE
od1.Role = 'admin'
ORDER BY
od1.ID;
Edit:
Coincidentally, for the exact data you showed us, you might also be able to simplify to:
SELECT ID, Hotel, Role, MIN(Email) AS Email
FROM OriginalData
WHERE Role = 'admin'
GROUP BY ID, Hotel, Role;
However, this only works because all the columns you want to appear are either aggregates or constants. If you had other columns specific to a matching minimum row, this would not work.
Well this is easy but with a twist (notice the the TOP 1...it will return just one record
SELECT TOP 1 *
FROM OriginalData
WHERE OriginalData.[Role])='admin'
The problem is that in order to get Random you need to use something for randomize it for some value ...here lets take ID
So the SQL will be :
SELECT TOP 1 *
FROM OriginalData
WHERE OriginalData.[Role])='admin' AND ID = Clng(Rnd() * Dmax("ID","OriginalData")

SQL Sort table by number of items in common

I have 3 tables, user, artist and a join table.
I'd like to find for a particular user, the ordering of the rest of the user table by the number of artists they have in common in the join table, or potentially just the n other users who are have the most in common with them.
For example in the table:
userID | artistID
-------|----------
1 | 1
1 | 2
2 | 1
2 | 2
3 | 1
I'd want to get that the ordering for user1 would be (2,3) because user2 shares both artist1 and artist2 with user1, whereas user3 only shares artist1.
Is there a way to get this from SQL?
Thanks
Assuming that you always know the user ID you want to check agaist, you can also do the following:
SELECT user, count(*) as in_common
FROM user_artist
WHERE
user<>1 AND
artist IN (SELECT artist FROM user_artist WHERE user=1)
GROUP BY user
ORDER BY in_common DESC;
This avoids joining which might have better performance on a large table. Your example is sqlfiddle here
You can do this with a self-join and aggregation:
select ua.userID, count(ua1.artistID) as numInCommonWithUser1
from userartists ua left join
userartists ua1
on ua.artistID = ua1.artistID and ua1.userID = 1
group by ua.userID
order by numInCommonWithUser1 desc;
If Suppose you know the user ID you are going to check then this query will complete your requirement and also perform very well.
SELECT ua1.user, count(*) as all_Common
FROM user_artist ua1
WHERE
(
Select count(*)
From user_artist ua2
Where ua2.user=1
AND ua2.artist=ua1.artist
)>0
AND ua1.user = 1
GROUP BY ua1.user
ORDER BY ua1.all_Common DESC;
Let me know if any question!

SQLite SQL UPDATE query based on the another table

So I have a few tables:
___ TABLE A (users info) ___
team_id | user_id | points | rewards
___ TABLE B (points for every event)___
team_id | user_id | points | rewards | event_type
___ Table C (users) ___
user_id | name | etc..
In the table A I have summary information for all the users based on the team. In the table B I have an atomic information for each event (something like a history). I would like to update information in the table A (points and rewards) by some of the same fields from table B using only user_id. My problem is that I can't understand how can I do it in one query.
For exemple I can make a query like
WITH storage as (
SELECT
sum(points) as points,
sum(rewards) as rewards,
team_id FROM B
WHERE user_id = 1 AND team_id = 1
)
UPDATE A
SET
points = (
SELECT points FROM storage
),
rewards = (
SELECT rewards FROM storage)
WHERE user_id = 1 and team_id = 1 ;
But I would like to run this action without team_id. For example I run sql like
WITH storage as (
SELECT
sum(points) as points,
sum(rewards) as rewards,
team_id FROM B
WHERE user_id = 1 GROUP BY team_id
)
And after that update points and rewards for each line in the table B based on the team_id.
Is it possible to make a query without loop in the back-end?
UPDATE:
It's for the SQLite database
UPDATE 2
you could find the response enter link description here
Like this?
update A set A.points = B.sumpoints, A.reward = B.sumreward
from A,
(select userid, teamid, sum(points) sumpoints, sum(reward) sumreward
from B group by userid, teamid) B
where A.userid = B.userid and A.teamid = B.teamid
So in the end I found the solution for the SQLite. It's really close to my first query.
WITH storage as (
SELECT
sum(points) as points,
sum(rewards) as rewards,
team_id FROM B
WHERE team_id IS NOT NULL
GROUP BY user_id, team_id
)
UPDATE A
SET
points = (
SELECT points FROM storage WHERE
storage.team_id = A.team_id AND storage.user_id = A.user_id
),
rewards = (
SELECT rewards FROM storage WHERE
storage.team_id = A.team_id AND storage.user_id = A.user_id
)
WHERE user_id = 1;
Also it's possible to remove unused data to add where statement to the WITH storage block (add filter by user_id )
WITH storage as (
SELECT
sum(points) as points,
sum(rewards) as rewards,
team_id FROM B
WHERE team_id IS NOT NULL AND user_id = 1
GROUP BY team_id
)
UPDATE A
SET
points = (
SELECT points FROM storage WHERE storage.team_id = A.team_id
),
rewards = (
SELECT rewards FROM storage WHERE storage.team_id = A.team_id
)
WHERE user_id = 1;

How to query two same values in SQL Server 2008?

I have table in SQL Server 2008 with following fields
RoomUserId->Primary key
RoomId->Foreign Key of Table Rooms
UserId->Foreign Key of Table Users
Now I have the values as following where the RoomId is common for both users
RoomUserId RoomId UserId
1 11 1
2 11 2
3 12 1
4 12 3
5 13 1
6 13 4
Now I need a SQL query to find the roomid of two users which is distinct. i.e, roomid of user 1 and user 2, roomid of user 1 and user 3.
Please anyone help me with is since I am new to SQL Server.
Try this:
SELECT r1.roomId FROM room r1 JOIN room r2 ON r1.roomId = r2.roomId WHERE r1.userId = 1 AND r2.userId = 3
If you mean find a roomid that is common to users:
select distinct f.roomid
from rooms f
inner join rooms s on f.roomid = s.roomid and f.userid <> s.userid
Or you can use grouping:
select roomid
from rooms
group by roomid
having count(distinct userid) > 1
If you only ever need rooms where there is more than one user then this will work:
SELECT DISTINCT RoomID
FROM RoomUser r1
INNER JOIN RoomUser r2
ON r1.RoomID = r2.RoomID
AND r1.RoomUserID != r2.RoomUserID
If you need the room ID of rooms with x users then use the Having Clause, this is more extensible than self joining e.g. if you need to find room IDs with 3 or more User IDs then you would end up with:
SELECT RoomID
FROM RoomUser
GROUP BY RoomID
HAVING COUNT(DISTINCT UserID) > 3
Whereas using self joins, while probably more efficient will end up with some quite messy SQL. Check execution plans and run some tests to see which is more efficient for your needs.
If you actually need the user IDs of the Users in Rooms with more than one User ID then you could use a CTE to build a comma separated string of users in each room:
;WITH RoomUserCTE AS
( SELECT RoomID,
MIN(UserID) [UserID],
CONVERT(VARCHAR(20), MIN(UserID)) [Users],
0 [Recursion]
FROM RoomUser
GROUP BY RoomID
UNION ALL
SELECT a.RoomID,
b.UserID [UserID],
CONVERT(VARCHAR(20), Users + ', ' + CONVERT(VARCHAR, b.UserID)),
Recursion + 1
FROM RoomUserCTE a
INNER JOIN RoomUser b
ON a.RoomID = b.RoomID
AND b.UserID > a.UserID
)
SELECT RoomID, Users
FROM ( SELECT *, MAX(Recursion) OVER(PARTITION BY RoomID) [MaxRecursion]
FROM RoomUserCTE
) cte
WHERE MaxRecursion = Recursion
For the data in your question this will yield
| RoomID | Users |
|---------+---------|
| 11 | 1, 2 |
| 12 | 1, 3 |
| 13 | 1, 4 |
This would work no matter how many user IDs were associated with the same Room ID, so again is more forward compatible.