I have a table of chat messages:
id, nToUserID, nFromUserID, strMessage
I'm trying find unique occurrences of messages between two users. NOT all the messages, just, is there at least one message to a user or from a user. I'll use this to show a list of "conversations" which could then be clicked to view the full chat thread.
I tried using a DISTINCT select, but that appeared to still give me multiple records between the same users.
I thought about a left JOIN, but again that appears to give me multiple or empty records.
If I understand correctly, you could use least() and greatest():
select distinct least(nToUserID, nFromUserID), greatest(nToUserID, nFromUserID)
from t;
If you want the other users, then use:
select distinct (case when nToUserID = ? then nFromUserID else nToUserID end) as userID
from t
where ? in (nToUserID, nFromUserID);
The ? is the id of the user you want the connections to.
You can use the count function to get the unique id pairs and their respective number of conversations.
SELECT nFromUserId, nToUserId, count(id) FROM `table` GROUP BY nFromUserId, nToUserId
Though this doesn't work if you need to count nFromUserId and nToUserId interchangeably.
Related
SQL query, find the user that does not have bank_account
I have two tables, the first one is table bank_customer, and the other is table user.
The table of bank_customer has many columns, including user_id and bank_account.
in table user, I have column id.
not every user has bank_account. so I want to query, users that do not have a bank_account, but it gets hard.
SELECT * FROM users, bank_customers
WHERE NOT users.id=bank_customers.user_id
so how can I query in SQL with this case?
and how to logic that case? for the next case I can find out by myself.
There are a few ways to go about that, but the simplest way would probably be using IN:
SELECT *
FROM users
WHERE id NOT IN (
SELECT user_id
FROM bank_customers
)
I've got three tables :
conversations (contains private conversations)
conversations-members - structure : convId | userId (contains all ids of members participating to a conversation so multiple people can talk together. One user participating to a conversation equals one row)
users (users table, classic)
What I am trying to do is :
Users have friends. So, the user browsing my application can open a conversation from his friends' ids.
So, first, I want to look up in conversations-members if there is an existing conversation ONLY between user's Id and his friend's Id and then, pick up the conversation id in conversations table that conversations-members gave me.
Is it possible to do this in one request? If I have to do two requests, I don't even know how to build the first one (find the rows that contain user's Id and friend's Id that have conversation Id in common).
My first idea was to make a single conversations table which would also contain member's Id in the form of a string like "55,105,85,22" and then parse it to get an array, but I think the way I want to do it gives me more options and could be simpler if I manage to handle the SQL requests I need.
To find conversations involving just two particular users, you can do something like this.
SELECT convId, COUNT(*)
FROM `conversations-members`
WHERE userId IN (FIRST_USER, SECOND_USER)
GROUP BY convId
HAVING COUNT(*) = 2
The HAVING line filters out any conversation with any users besides the two you want.
You can use that as a subquery:
SELECT whatever
FROM conversations
WHERE convId IN (
SELECT convId
FROM `conversations-members`
WHERE userId IN (FIRST_USER, SECOND_USER)
GROUP BY convId
HAVING COUNT(*) = 2
)
If you wanted conversations with your two users and any other users, you could change the subquery to HAVING COUNT(*) >= 2.
Pro tip: SQL thinks hyphens - mean subtraction. Avoid them in column and table names.
It's not recommended to use a column with multiple values.
When you have a relation 1-N, normally you need to use a table with two keys, for example, ConvID - UserID. Your index are created by these two columns, for don't allow to generate a duplicate (an UserID two times on the same ConvID, for example).
But, in response to your question, yes, it's possible to do that search. You need to search for an entry on your SQL table where two different IDs are on the same line. You can do it with LIKE clause, but it's not a good option (I think it's the worst possible WHERE clause).
The syntax will be as follows:
SELECT convId FROM conversations-members WHERE userId LIKE '%id1%' AND userId LIKE '%id2%'
For some additional recommendation, if your conversations are always a pair of people, use two new fields on your "conversations" table. If your conversation can have from one to N members, use the "conversations-members" as suggested at the start of the answer.
What I'm trying to do is that I have a table called messages. It has 5 fields ID, SENDER, RECIPIENT, MESSAGE and TIMESTAMP. Each user gets an id when he uses this app. Each message that he sends using this app gets stored in this table with the id of the recipient.
Now what I want is to be able to list the most recent message of each conversation and then list each conversation in order of the recency of their last message. Lets call this table CONVERSATIONS.
Each conversation between two users XX and YY is defined as a table generated by
SELECT * FROM MESSAGES WHERE SENDER=XX and RECIPIENT=YY ORDER BY TIMESTAMP
In CONVERSATIONS table we only want the most recent message of each of the conversations in the MESSAGES table.
I don't want to use PHP and was looking for a solution only using MySQL. Thank you.
If I understood you correctly, you may try something like this.
select MESSAGES.*,
concat(SENDER|RECIPIENT, "r", SENDER&RECIPIENT) as combo
from MESSAGES inner join (
select max(TIMESTAMP) maxtime,
concat(SENDER|RECIPIENT, "r", SENDER&RECIPIENT) as combo2
from MESSAGES group by combo2
) subq
on combo = combo2 and MESSAGES.TIMESTAMP = subq.maxtime
order by MESSAGES.TIMESTAMP desc
Here standard technique of grouping is used (from here), but also I used combo expression, which is commutative injection from (SENDER,RECIPIENT) to strings.
It'll return you all rows with max timestamps grouped by unordered pair {SENDER,RECIPIENT}
Please forgive my ignorance here. SQL is decidedly one of the biggest "gaps" in my education that I'm working on correcting, come October. Here's the scenario:
I have two tables in a DB that I need to access certain data from. One is users, and the other is conversation_log. The basic structure is outlined below:
users:
id (INT)
name (TXT)
conversation_log
userid (INT) // same value as id in users - actually the only field in this table I want to check
input (TXT)
response (TXT)
(note that I'm only listing the structure for the fields that are {or could be} relevant to the current challenge)
What I want to do is return a list of names from the users table that have at least one record in the conversation_log table. Currently, I'm doing this with two separate SQL statements, with the one that checks for records in conversation_log being called hundreds, if not thousands of times, once for each userid, just to see if records exist for that id.
Currently, the two SQL statements are as follows:
select id from users where 1; (gets the list of userid values for the next query)
select id from conversation_log where userid = $userId limit 1; (checks for existing records)
Right now I have 4,000+ users listed in the users table. I'm sure that you can imagine just how long this method takes. I know there's an easier, more efficient way to do this, but being self-taught, this is something that I have yet to learn. Any help would be greatly appreciated.
You have to do what is called a 'Join'. This, um, joins the rows of two tables together based on values they have in common.
See if this makes sense to you:
SELECT DISTINCT users.name
FROM users JOIN conversation_log ON users.id = converation_log.userid
Now JOIN by itself is an "inner join", which means that it will only return rows that both tables have in common. In other words, if a specific conversation_log.userid doesn't exist, it won't return any part of the row, user or conversation log, for that userid.
Also, +1 for having a clearly worded question : )
EDIT: I added a "DISTINCT", which means to filter out all of the duplicates. If a user appeared in more than one conversation_log row, and you didn't have DISTINCT, you would get the user's name more than once. This is because JOIN does a cartesian product, or does every possible combination of rows from each table that match your JOIN ON criteria.
Something like this:
SELECT *
FROM users
WHERE EXISTS (
SELECT *
FROM conversation_log
WHERE users.id = conversation_log.userid
)
In plain English: select every row from users, such that there is at least one row from conversation_log with the matching userid.
What you need to read is JOIN syntax.
SELECT count(*), users.name
FROM users left join conversion_log on users.id = conversation_log.userid
Group by users.name
You could add at the end if you wanted
HAVING count(*) > 0
user table
user_id
entry
user_id
points_a
points_b
SELECT user.*,
(SUM(entry.points_a) + SUM(entry.points_b)) as points_total
FROM user
LEFT JOIN entry on entry.user_id = entry.user_id
..is what I'm trying to do — get a total count of all points that a user has. The field type for points is INT. This Doesn't seem to work?
Given that you have no columns in user except the ID, the join really serves no purpose.
select userid, SUM(points_a) + SUM(points_b) as total
from entry
group by userid
This will give you what you are looking for. If you need more fields from the user table that you just didn't show, you can do the join and add those fields to the select.
I think what you were missing was the Group By clause though.