MySql multiple GROUP BY alternative - mysql

I have two tables: teams and messages
teams
========
id | name
messages
============================
id | team_id | created_date
I need a result set that looks like this:
team name | number of messages | most recent message
For example:
Joe Blow's Group | 23 | 2018-08-29
My initial try at this:
select team.id, team.name, count(messages.team_id) as 'Number of
Messages', messages.created_date as 'Last Message' from messages inner join
teams on messages.team_id = team.id group by team.name
Produced:
Joe Blow's Team | 23 | 2018-02-14
Bob's Team | 12 | 2018-04-15
etc etc
The issue is that the dates are of the oldest message created for that team, not the most recent. If I try to add sort by it only sorts this result set but the wrong dates. If I try multiple 'group by' I get a row for every message.
Does this need a subquery or is there a simpler way to have the created_date be the most recent?

Use MAX function to get the latest created_date.
SELECT group.id,
group.name,
COUNT(messages.group_id) AS 'Number of Messages',
MAX(messages.created_date) AS 'Last Message'
FROM messages
INNER JOIN groups ON messages.group_id = group.id
GROUP BY group.name

Related

SQL select query to get total unique entries from two foreign key tables based on a column value

I've got three tables as shown below - ideally it wouldn't be laid out like this but currently no power change it.
Team User Member
ID | Name ID | TeamId | Email ID | TeamId | Email
----------
1 | Team A 1 | 1 | a#email.com 1 | 1 | a#email.com
2 | Team B 2 | 1 | b#email.com 2 | 1 | b#email.com
3 | Team C 3 | 1 | c#email.com
I need to be able to get the combined count of users and members in each team, uniquely based on their email address.
So for example, Team A would have a unique count of combined members and users of 3.
An entry may exist in either the user table OR the member table, or in both for each email.
The outcome of the query would be TeamName and TotalUsers.
Any help with this type of query would be greatly appreciated.
Use UNION to collect all the distinct combinations of team ids and emails from User and Member and do a LEFT join of Team to that resultset and aggregate:
SELECT t.id, t.name,
COUNT(email) count
FROM Team t
LEFT JOIN (
SELECT teamid, email FROM User
UNION
SELECT teamid, email FROM Member
) e ON e.teamid = t.id
GROUP BY t.id;
you can UNION members and User table, so that duplicates would be removed
And then join it to the temas table
SELECT
t1.Name, COUNT(DISTINCT Email)
FROM
Team t1
JOIN
( SELECT TeamId , Email FROM User
UNION SELECT TeamId , Email FROM Member) t2 ON t2.TeamId = t1.ID
GROUP BY t1.Name

MySQL: Get latest message from 2 tables that are associated with eachother

First, thank you so much for your help.
I have 2 tables: a conversation table and a message table, and a third table assoc_message__conversation that associates the messages to a conversation.
I need to get the latest message_id and message sent for each conversation specified, along with the conversation_id it is associated with.
Here is a db-fiddle: https://www.db-fiddle.com/f/kxRQeGUYYgQ7FTwi96hbLp/0
As you can see in this example, there are two conversations with conversation_id of 1 and 2, and there are three messages associated to each conversation. Messages 1, 2, and 3 are associated to conversation 1, and messages 4, 5 and 6 are associated to conversation 2.
I need to be able to specify the conversation_id's in the assoc_message__conversation table (IDs 1 and 2), and retrieve the latest message_id, message and the associated conversation_id sent from the message table for each conversation specified.
So the rows it should pull are:
conversation_id | message_id | message
------------------------------------------------
1 | 3 | "Latest message"
------------------------------------------------
2 | 6 | "Latest message"
------------------------------------------------
Thank you so much for your help!
In older versions of MySQL (< 8.0.2), we can use Derived Tables. In a Derived table, we can get the latest send_datetime value for each conversation_id. Also, it is noteworthy that you can provide your filters for conversation_id in the WHERE clause of this subquery.
We can then use this subquery`s result-set and join back to the main tables appropriately, to get the row corresponding to the latest message in a conversation.
Schema (MySQL v5.7)
View on DB Fiddle
Query #1
SELECT
amc.conversation_id,
m.message_id,
m.message
FROM
assoc_message__conversation AS amc
JOIN message AS m
ON m.message_id = amc.message_id
JOIN
(
SELECT
amc1.conversation_id,
MAX(m1.send_datetime) AS latest_send_datetime
FROM
assoc_message__conversation AS amc1
JOIN message AS m1
ON m1.message_id = amc1.message_id
WHERE amc1.conversation_id IN (1,2) -- Here you provide your input filters
GROUP BY amc1.conversation_id
) AS dt
ON dt.conversation_id = amc.conversation_id AND
dt.latest_send_datetime = m.send_datetime;
Result
| conversation_id | message_id | message |
| --------------- | ---------- | -------------- |
| 1 | 3 | Latest message |
| 2 | 6 | Latest message |
In MySQL 8.0.2 and above, we can use Row_Number() functionality. Within a partition of conversation_id, we will determine Row Number for every message, sorted in descending order of send_datetime. In this subquery, you can provide your filters for conversation_id in the WHERE clause.
We will then use this result-set as a Derived Table, and consider only those rows, where Row Number value is 1 (as it will belong to latest send_datetime).
Schema (MySQL v8.0)
View on DB Fiddle
Query #2
SELECT
dt.conversation_id,
dt.message_id,
dt.message
FROM
(
SELECT
amc.conversation_id,
m.message_id,
m.message,
ROW_NUMBER() OVER (PARTITION BY amc.conversation_id
ORDER BY m.send_datetime DESC) AS row_no
FROM
assoc_message__conversation AS amc
JOIN message AS m
ON m.message_id = amc.message_id
WHERE amc.conversation_id IN (1,2) -- Here you provide your input filters
) AS dt
WHERE dt.row_no = 1;
Result
| conversation_id | message_id | message |
| --------------- | ---------- | -------------- |
| 1 | 3 | Latest message |
| 2 | 6 | Latest message |
Assuming that amc_id increments for each new message, I would recommend a correlated subquery in the where clause:
select amc.*, m.message
from message m join
assoc_message__conversation amc
on amc.message_id = m.message_id
where amc.amc_id = (select max(amc.amc_id)
from assoc_message__conversation amc2
where amc2.conversation_id = amc.conversation_id
);
If you actually need to use send_datetime, then an additional join is necessary:
where m.send_datetime = (select max(m2.send_datetime)
from message m2 join
assoc_message__conversation amc2
on amc2.message_id = m2.message_id
where amc2.conversation_id = amc.conversation_id
)

mysql group by query (very close)

I have 3 tables (simplified):
orders (orderid, date)
orderlines (lineid, orderid, userid)
users (userid, name)
I would like a list of numbers of orders per user grouped by year and month orders by username. The reason why order lines have the user id is that an order line can be changed by another user. The following gives me a list of numbers of order lines, but it needs to count the orders only:
SELECT
users.name,
COUNT(orders.orderid) AS number,
MONTH(orders.date) AS month,
YEAR(orders.date) AS year
FROM orders
INNER JOIN orderlines ON orders.orderid = orderlines.orderid
INNER JOIN users ON orderlines.userid = users.userid
GROUP BY orderlines.userid, YEAR(order.date), MONTH(order.date)
ORDER BY users.name, orders.date
I'm feeling I'm very close...
Current output is:
name | number | month | year
----------------------------
jeff | 1000 | 1 | 2018
jeff | 1100 | 2 | 2018
Should be:
name | number | month | year
----------------------------
jeff | 100 | 1 | 2018
jeff | 110 | 2 | 2018
The number currently shown is the number of order lines, but I would like it to be the number of orders.
Try below: you need to use users.name in group by, also it should be in your order by clause as your question says it, another thing is as you want to know the count of order per user, so it should be count distinct orderid
SELECT users.name, COUNT(distinct orders.orderid), MONTH(orders.date), YEAR(orders.date)
FROM orders
INNER JOIN orderlines ON orders.orderid = orderlines.orderid
INNER JOIN users ON order lines.userid = users.userid
GROUP BY users.name, YEAR(order.date), MONTH(order.date)
ORDER BY users.name

mysql using limit in a left join not working properly

I have two tables looking like this
Patient (table 1)
id | name
------------
1 | robel
2 | dave
Patient_followup (table 2)
id | Patient_id | date_created
-----------------------
1 | ---- 1 -- | 01/01/2015
2 | -----1 -- | 01/07/2016
I want to display all the patients with their perspective latest followup data. so i tried using this query
Select * from patient
left join Patient_followup pf on pf.Patient_id = patient.id
order by pf.date_created
Limit 1
but this is giving me only the first patient robel. i tryed removing the limit and its giving me two records of robel and one record of dave because robel has two followup data. so what should i do to get only one record of each patient ?
Try this:
Select
*
from
patient
left join
(SELECT
id as pf_id,
MAX(date_created) as latest_followup_date,
Patient_id
FROM
Patient_followup
GROUP BY
Patient_id) as pf
ON pf.Patient_id = patient.id
As mentioned by anton in the first comment, you need to use aggregation to get one record per patient.
Select patient.*,MAX(pf.date_created) as followupdate,group_concat(pf.date_created) from patient
left join Patient_followup pf on pf.Patient_id = p.patient.id
group by patient.id
order by pf.date_created
Here, you will get your values comma separated.
1) "Limit 1" will only return the first result. Typically this is used if the query will result in a very large result set and you only want the first few results.
Ex:
"LIMIT 30" will show the first 30 rows of the query.
2) I would change to setup of the tables so the query is smoother. Right now, you create a new line for each follow-up date even if the patient is already created. You could add another column in the table named "FollowUpDate". That way each patient record has the table id, patient id, creation date and followup date in the same row. That way, each patient has only one row.
EX:
Patient (table 1)
id | name | created_date | next_followup_date |
1 | Robel | 01/01/2015 | 01/01/2016 |
2 | Dave |[created_date]| [next_follup_date] |
Patient_followup (table 2)
id | Patient_id | date_created | followUpDate |
1 | 1 | 01/01/2015 | 06/01/2016 | // example date
2 | 1 | 01/01/2015 | 01/01/2016 |
3 | 2 |[date created]| [FollowUpDate] |
3) Change query to:
Use this select statement to get all patient records.
Select * from patient
left join Patient_followup pf on pf.Patient_id = patient.id
order by pf.Patient_id
Use this select statement to get the specific patient record information.
Select * from patient
inner join Patient_followup pf on pf.Patient_id = patient.id
where patient.id = 1 //to get robel. Edit this line as necessary, perhaps by user input...
order by pf.followUpDate
NOTE: When you insert a new record in Patient_followup, make sure you update Patient.next_followup_date.
I hope this helps!

How to group by rows but with the most recent date?

In my MySQL database I have a table like this used for storing conversation messages from any people
id int(11) id of the message
from member_id int(11) id of the person the message was sent from
to member_id int(11) id of the person the message was sent to
date sent datetime date of when it was sent
active tinyint(1) if the message is deleted
text longtext the text of the message
from_read tinyint(1) boolean to know if the person who sent it read it
to_read tinyint(1) boolean to know if the person who it got sent to read it
So for example, it could have like:
from_member_id to_member_id date sent
1 2 june 12
1 3 june 13
2 3 june 14
3 1 june 9
So we have a conversation between person 1 and 2, 1 and 3, 2 and 3.
I am trying to get a select statement which will give me the most recent message that the current user is involved with from every conversation that user is in. So if 1 is logged in then I would expect to get 2 rows. The first row in the result set would be the second row above (july 13) because its the most recent, then then the second row in the result set would be the first row above (june 12), which are the most recent from 1's two conversations. The result set also needs to be sorted by date sent, so newer conversations are listed on top.
What I am trying to do is like the texting in android phones, where you see the list of conversations, and the most recent message in each listing.
This is my sql query
SELECT *
FROM (
SELECT *
FROM message
WHERE `from member_id`=1 OR `to member_id`=1
ORDER BY IF(`from member_id`=1, `to member_id`, `from member_id`)
) as t
GROUP BY IF(`from member_id`=1, `to member_id`, `from member_id`)
I just hardcoded 1 for now to be the current user. What I am doing is, sorting them by the id of the other person which I can check using the if statement, then grouping that result so I try to get the recent one from each conversation.
The problem is that when grouping, each group can have more than 1 rows, and it just seems to pick some random row. How can I get it to pick the row that has the most recent date sent value?
Are you looking for something like this?
SELECT m.*
FROM message m JOIN
(
SELECT from_member_id, to_member_id, MAX(date_sent) date_sent
FROM message
WHERE from_member_id = 1
GROUP BY from_member_id, to_member_id
) q
ON m.from_member_id = q.from_member_id
AND m.to_member_id = q.to_member_id
AND m.date_sent = q.date_sent
ORDER BY date_sent DESC
Sample output:
| FROM_MEMBER_ID | TO_MEMBER_ID | DATE_SENT |
----------------------------------------------
| 1 | 3 | 2013-06-13 |
| 1 | 2 | 2013-06-12 |
Here is SQLFiddle demo
UPDATE
SELECT m.*
FROM message m JOIN
(
SELECT LEAST(from_member_id, to_member_id) least_id,
GREATEST(from_member_id, to_member_id) greatest_id,
MAX(date_sent) date_sent
FROM message
WHERE from_member_id = 1
OR to_member_id = 1
GROUP BY LEAST(from_member_id, to_member_id),
GREATEST(from_member_id, to_member_id)
) q
ON LEAST(m.from_member_id, m.to_member_id) = q.least_id
AND GREATEST(m.from_member_id, m.to_member_id) = q.greatest_id
AND m.date_sent = q.date_sent
ORDER BY date_sent DESC
Sample output:
| FROM_MEMBER_ID | TO_MEMBER_ID | DATE_SENT |
----------------------------------------------
| 3 | 1 | 2013-06-14 |
| 1 | 2 | 2013-06-12 |
Here is SQLFiddle demo
SELECT
*
FROM message m INNER JOIN
(
SELECT
from_menber_id,
MAX(date_sent) AS sentdate
FROM message s
GROUP BY from_menber_id
) AS a
ON m.date_sent = a.sentdate AND a.from_menber_id = m.from_menber_id