selecting rows from table which have distinct values for a column - mysql

In my thread based messaging system, the table schema is
> messages table
id(int auto incr primary key)
body(varchar)
time(datetime)
>message_reference table
id(int auto incr primary key)
message_id(forgain key from message table)
sender
receiver
Here, I want to select the first message id which is sent to a new receiver and sender is the user who is logged in.
Doing this with multiple queries and some code is obviously possible but can it be done with a single query for performance issues??

You can try
EDIT:
If the id is auto increment, then the id will also increase with time and you can use:
SELECT message_reference.message_id, message_reference.receiver, messages.body
FROM message_reference, messages
WHERE message_reference.message_id IN (SELECT MIN(message_reference.message_id)
FROM message_reference
GROUP BY message_reference.receiver)
AND message_reference.message_id = messages.id AND message_reference.sender = <sender>

Here's my best guess as to what you want, but it would be easier if you gave known inputs, example data, and expected output.
SELECT
MR2.message_id
FROM (
SELECT
MR.sender,
MR.receiver,
M.MIN(`time`) AS min_time
FROM
Message_References MR -- Either use plural names (my personal preference) or singular, but don't mix them
INNER JOIN Messages M ON
M.id = MR.message_id
WHERE
MR.sender = <sender>
GROUP BY
MR.received) SQ
INNER JOIN Message_References MR2 ON
MR2.sender = SQ.sender AND
MR2.receiver = SQ.receiver AND
MR2.`time` = SQ.min_time

select mr.message_id from
message_reference as mr inner join
(select mr1.reciever max(m1.time) as time from messages as m1
inner join message_reference as mr1 on mr1.message_id = m1.id
group by mr1.reciever) as last
on mr.reciever = last.reciever and mr.time = last.time
join message reference with "maxtime per reciever" table on reciever and time

Well I got the answer, Just a group by query worked the way I wanted. I used query
SELECT SENDER,
RECEIVER,
BODY,
TIME,
MESSAGE_ID
FROM MESSAGE_REF JOIN MESSAGE
ON MESSAGE.ID=MESSAGE_REF.MESSAGE_ID
ORDER BY 'TIME' GROUP BY RECEIVER`
Thanks everyone for the help.

Related

Beginner SQL: JOIN clause skewing results of query

thank you all for taking the time to read and help if you can! I have a query below that is getting large and messy, I was hoping someone could point me in the right direction as I am still a beginner.
SELECT
DATE(s.created_time_stamp) AS Date,
s.security_profile_id AS Name,
COUNT(*) AS logins,
CASE
WHEN COUNT(s.security_profile_id) <= 1
THEN '1'
WHEN COUNT(s.security_profile_id) BETWEEN 2 AND 3
THEN '2-3'
ELSE '4+'
END AS sessions_summary
FROM session AS s
INNER JOIN member AS m
ON s.security_profile_id = m.security_profile_id
JOIN member_entitlement AS me ON m.id = me.member_id
JOIN member_package AS mp ON me.id = mp.member_entitlement_id
**JOIN member_channels AS mc ON mc.member_id = m.id**
where member_status = 'ACTIVE'
and metrix_exempt = 0
and m.created_time_stamp >= STR_TO_DATE('03/08/2022', '%m/%d/%Y')
and display_name not like 'john%doe%'
and email not like '%#aeturnum.com'
and email not like '%#trendertag.com'
and email not like '%#sargentlabs.com'
and member_email_status = 'ACTIVE'
and mp.package_id = 'ca972458-bc43-4822-a311-2d18bad2be96'
and display_name IS NOT NULL
and s.security_profile_id IS NOT NULL
**and mc.id IS NOT NULL**
GROUP BY
DATE(created_time_stamp),
Name
ORDER BY
DATE(created_time_stamp),
Name
The two parts of the query with asterisks are the two most recently added clauses and they skew the data. Without these, the query runs fine. I am trying get a session summary which works fine, but I only want the sessions of people who have a 'channel' created. Maybe mc.id IS NOT NULL is not the way to do this. I will share my query that shows me how many people have created channels. Essentially, I am trying to combine these two queries in the cleanest way possible. Any advice is greatly appreciated!
-- Users that have Topic Channels and Finished Set Up FOR TRIAL DASH**
select count(distinct(m.id)) AS created_topic_channel
from member m right join member_channels mc on mc.member_id = m.id
left join channels c on c.id = mc.channels_id
JOIN member_entitlement AS me ON m.id = me.member_id
JOIN member_package AS mp ON me.id = mp.member_entitlement_id
where title not like '# Mentions'
and member_status = 'ACTIVE'
and metrix_exempt = 0
and m.created_time_stamp >= STR_TO_DATE('03/08/2022', '%m/%d/%Y')
and display_name not like 'john%doe%'
and email not like '%#aeturnum.com'
and email not like '%#trendertag.com'
and email not like '%#sargentlabs.com'
and member_email_status = 'ACTIVE'
and display_name IS NOT NULL
and mp.package_id = 'ca972458-bc43-4822-a311-2d18bad2be96';
The metric I am trying to retrieve from the DB is how many users have created a channel and logged in at least twice. Thank you again and have a wonderful day!!
If id is the primary key of member_channels then it does not make sense to check if it is null.
If all you want is to check whether a member has a 'channel' created, then instead of the additional join to member_channels, which may cause the query to return more rows than expected, you could use EXISTS in the WHERE clause:
where member_status = 'ACTIVE'
and .......................
and EXISTS (SELECT 1 FROM member_channels AS mc WHERE mc.member_id = m.id)
I would guess your tables aren't at the same level of granularity. A member may have many sessions, and 0-many channels.
eg if member 123 has five sessions and creates three channels => 15 rows of data in this join.
To adjust for this, it's best practice to join on the same level of granularity. You could roll up sessions to the member level, channels to the member level, and then join both against members.

how can i find unique conversation from table in sql

I Have this table:
[Messages table]
I need to find the number of uniqe conversation -
conversation is define as senderid sent msg to reciverid, and reciverid has replied (no matter how many times or the thread length, it will be count as 1 conversation).
so if senderid = 1, reeiver id =2
and in the next row senderid = 2 and reciever id =1 this is one conversation (till the end of time)
Im really stock and not sure how to proceed.
Thanks!
You can use the functions LEAST() and GREATEST() to create unique combinations of the 2 ids and aggregate:
SELECT COUNT(DISTINCT LEAST(m1.senderid, m1.receiverid), GREATEST(m1.senderid, m1.receiverid)) counter
FROM Messages m1
WHERE EXISTS (SELECT 1 FROM Messages m2 WHERE (m2.receiverid, m2.senderid) = (m1.senderid, m1.receiverid))
See the demo.
Results (for your sample data):
counter
2
Here's one option using a self join with least and greatest to get your desired results:
select count(*)
from (
select distinct least(m1.senderid, m2.senderid), greatest(m1.receiverid, m2.receiverid)
from messages m1
join messages m2 on m1.senderid = m2.receiverid and m1.receiverid = m2.senderid
) t

How Can I get messages in form of conversation from below table structure?

Here is mySql Table structure (pardon for errors its a legacy system) inbox_id is auto incremented primary key. I tried this query after some research but it is only giving latest message.
SELECT a.inbox_id, msg_to, a.msg_from, a.msg_title, a.msg_body
FROM `inbox` as a
Inner join
(
select max(inbox_id) as id
from inbox as alt
where alt.msg_to= 12
or alt.msg_from='12'
GROUP BY least(`msg_to` ,`msg_from`), greatest(`msg_to` , `msg_from`)
)b
on a.inbox_id = b.id

SQL and HQL retrieve data from two tables

I've two tables in Mysql.
message -> msg_id(pk) and message(varchar)
track_record -> tr_id(pk), msg_id (foreign key), object_id (to whom the message is sent), profile_id(who sent the message)
Now i want to create a single query which gives me the messages and the sender profile id given the object_id.
E.g say i have the object_id 1.
Now i want to see all the message sent to the user with id 1.
Edited the answer to add my query which i tried
SELECT m.message, u.profile_profile_id FROm `message` as m, `user_track_record` as u
WHERE msg_id IN
(SELECT message_msg_id FROM user_track_record WHERE object_profile_id = 1)
and u.profile_profile_id IN
(SELECT profile_profile_id from `user_track_record` WHERE object_profile_id = 1)
Want to do this in both SQL and HQL.
Any help appreciated. Thanks
Your SQL query is much too complex.
Start with simple things:
Select all the track_records sent to user 1:
select tr.* from track_record tr where tr.object_id = 1
Now you would like the message linked to the track record. This is done using a join:
select tr.*, m.* from track_record tr
inner join message m on m.msg_id = tr.msg_id
where tr.object_id = 1
Now you would like only the ID of the sender, and the body of the message:
select tr.profile_id, m.message
from track_record tr
inner join message m on m.msg_id = tr.msg_id
where tr.object_id = 1
In HQL, the query is almost identical. You have a Message entity and a TrackRecord entity. TrackRecord has a ManyToOne association with Message. It also has a ManyToOne association with the sender and the receiver
select tr.sender.id, m.message
from TrackRecord tr
inner join tr.message m
where tr.receiver.id = 1

Need some support writing a MySQL-Query

I'm writing a basic message-script in PHP/MySQL but I'm stuck at a database query right now. I'll appreciate any hints or assistance (:
I'm using two tables, since a message can be sent to several users:
messages:
id | sender_id | subject | ...
message_receivers:
message_id | receiver_id | ...
What I want to do now is display a message to the user that he selects. But I want to show the whole message history the user had in that conversation (jumping in browser to the one he selected). Doing this with a join is quite simple:
SELECT * FROM messages
JOIN message_receivers
ON messages.id = message_receivers.message_id
WHERE sender_id = x
AND receiver_id = y
But now I'm missing the information of other receivers of a message! And I have no clue how to get this information. Any ideas for that? (:
Join the message_receivers table one more time to retrieve the other recipients of the message:
SELECT
m.id, m.sender_id, m.subject,
r.receiver_id AS recipient,
c.receiver_id AS carboncopy
FROM messages AS m
INNER JOIN message_receivers AS r
ON m.id = r.message_id
LEFT OUTER JOIN message_receivers AS c
ON r.message_id = c.message_id AND r.receiver_id != c.receiver_id
WHERE m.sender_id = x AND r.receiver_id = y
The recipient that your are interested in will be in column recipient (in every result record). Other recipients will be in column carboncopy (one per result record). If carboncopy is NULL, then the message had only a single receiver.
If you want to see all the receivers of a message then remove the second part of the were clause:
AND receiver_id = y
at the same time you will want to specify the message_id because this will be to confusing to the user on the front end
AND message_id = z
You're missing information about other receivers of the message because of the clasue:
AND
receiver_id = y
This restricts the result set to just receiver y. Remove the clause, and you'll get them all. However you'll probably get every message sent where sender_id = x as well, so you'll need to limit the query by specifying a message_id.
So your final query should look something like this:
SELECT
*
FROM
messages
JOIN message_receivers ON messages.id = message_receivers.message_id
WHERE
sender_id = x
AND
message_id = y
you don't need to restrict your result to receiver_id = y, do you?
Also you might write the statement in a different way and easily return receivers ids:
SELECT m.*, r.receiver_id
FROM messages m, message_receivers r
WHERE m.id = r.message_id
AND m.sender_id = x