MySQL Left Join with latest record - mysql

Given two tables A and B, I want all the records from A where A.Param = "X". I also want a LEFT JOIN on B where B contains records of trials by various A.Ids for various experiments m, n, o...
The records on B have a time stamp as B.TrialTime (DateTime). As the design goes, there can be multiple trials for the same experiment by the same A.Id in B and for the purposes of the LEFT JOIN I need only the latest trial. This is what I came up with:
SELECT A.*, B.Experiment, B.Response, B.Evaluation, MAX(B.TrialTime) FROM A
LEFT JOIN B ON B.UserID = A.ID
WHERE A.Param = "X" GROUP BY concat(B.UserID, B.Experiment)
The problem is, it no longer acts as a LEFT JOIN, i.e. I am not getting all the users from A, even if they don't have any record in B, which is what I need. Any help?

Without access to the specifics of your tables, the problem you face is knowing "the most recent" trial (in table B)
In many rdbms one can use ROW_NUMBER() to solve this problem, but as yet MySQL does not supply that useful function. So you need to do something like this:
SELECT
a.*
, b.*
FROM A
LEFT JOIN
(
SELECT
bb.*
FROM B bb
INNER JOIN
(
SELECT
UserID
, MAX(TrialTime) MostRecent
FROM B
GROUP BY B.UserID
) bmax ON bb.UserID = bmax.UserID
AND bb.TrialTime = bmax.MostRecent
) b ON B.UserID = A.ID
Basically it isn't possible to get the MAX() of some columns, and all the other column values "from that row", at the same time. You need to get the MAX(TrialTime) then join back to the same table to get the appropriate rows.
For comparison, if row_number() was available, this is how it might be done:
SELECT
a.*
, b.*
FROM A
LEFT JOIN
(
SELECT
bb.*
, ROW_NUMBER() OVER (PARTITION BY bb.userid
ORDER BY bb.TrialTime DESC) AS rn
FROM B bb
) b ON B.UserID = A.ID
AND b.rn = 1
By the way, there isn't any specific reason given for using a LEFT [OUTER] JOIN. If you want rows from A that have NO joined data in B, then you would need an outer join, otherwise you should use an INNER JOIN for efficiency.

Related

How to return null on many to many join within window partition

I am creating a many to many join based on date, and then running a partition based on the user_id. The problem is that I need there to be an entry for each date, even if there is no data.
color_table
guess_table
Desired output (join with partition over user)
The problem is that, because user 1 didn't guess on 10-26-2021 and user 2 didn't guess on 10-28-2021, the rows with null simply don't exist. And I need the rows to be present as I'm doing operations within the window based on the date, so I need to see every date for a user, even if there's no guess.
Here is the fiddle with the window function and join:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b9f7f9549a0793aa16335141d86b7352
Here is the call I'm trying:
select dayt, user, cnt, guess, ROW_NUMBER() OVER (PARTITION BY user ORDER BY dayt) AS rowi
from color_table
left join guess_table
on dayt = dait;
You need all the combinations of the rows of color_table with each user and you can do this with a CROSS JOIN.
Then do a LEFT join to guess_table:
SELECT c.dayt, u.user, c.cnt, g.guess,
ROW_NUMBER() OVER (PARTITION BY u.user ORDER BY c.dayt) AS rowi
FROM color_table c
CROSS JOIN (SELECT DISTINCT user FROM guess_table) u
LEFT JOIN guess_table g
ON g.dait = c.dayt AND g.user = u.user;
See the demo.
Because the data you want to be shown does not exist for the left join / join operation you have to create it first. You do so by using a CROSS JOIN operation and then left joining with the existing tables:
select cj.dayt, cj.user, c2.cnt,
g2.guess,
ROW_NUMBER() OVER (PARTITION BY cj.user ORDER BY dayt) AS rowi
from
(select distinct c.dayt, g.user
from color_table c cross join guess_table g) cj
left join color_table c2 on c2.dayt = cj.dayt
left join guess_table g2 on g2.dait = cj.dayt and g2.user = cj.user;
See it working here: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=1ee0ee72007d3d17e50beb990f6691c2

returning all rows in table 1, when join most recent record in table 2

I've the following code, which pulls out the most recent row from a table called wwlassessments.
It works, but what I'm trying to do is show all the rows matching the WHERE criteria in table, regardless of whether there's an entry in the wwlassessments table.
I've tried changing the 2nd JOIN to a LEFT JOIN, but this just provides thousands of inaccurate results.
I'm sure it's very simple, but I can't for the life of me work out what I need to change! Thanks in advance.
SELECT s.*,
a.*
FROM wwlstatements s
LEFT JOIN wwlassessments a ON a.id = s.id
JOIN (SELECT n.id,n.pupilID,
MAX(n.dateAchieved) AS max_achieved_date
FROM wwlassessments n
where n.pupilID='114631705547'
GROUP BY n.id) y ON y.id = a.id
AND y.max_achieved_date = a.dateAchieved
WHERE s.`category`='Reading'
ORDER BY s.`statementID` ASC

mysql leftjoin by max autoincrement id?

A) users
B) subscribtion
C) package information
I've table where B is a link between A and C , the query selected from A where B has row id for A and join C by B id .
Full example :
SELECT a.*,c.packageTitle
FROM users AS a
LEFT JOIN subscribe AS b ON (b.userid = a.userid)
LEFT JOIN package AS c ON (b.packageid = c.packageid)
my problem if user has multi subscription in C, i cannot get latest subscription row in loop query, i also used MAX(c.packageid) inside SELECT failed also .
Goal : get latest record in B assigned by A id .
any advice is very much appreciated
I don't think you were far off by trying to use MAX() to obtain the record with the latest package ID (which assumes that this ID is an increasing auto-increment column). In my answer below, the subquery identifies the latest package ID for each user record, using a GROUP BY. This subquery is then used to filter the correct records from your original query.
SELECT a.userid, b.*
FROM users AS a
LEFT JOIN subscribe AS b
ON b.userid = a.userid
LEFT JOIN package AS c
ON b.packageid = c.packageid
INNER JOIN
(
SELECT a.userid, MAX(c.packageTitle) AS maxPackageTitle
FROM users AS a
LEFT JOIN subscribe AS b
ON b.userid = a.userid
LEFT JOIN package AS c
ON b.packageid = c.packageid
GROUP BY a.userid
) t
ON a.userid = t.userid AND c.packageTitle = t.maxPackageTitle
As a note, this query would greatly benefit from something called a Common Table Expression (CTE), which is available in other RBDMS such as SQL Server and Oracle. A CTE would make the query much less repetitive and more readable.
This should be simple enough. In Mysql you just as you said put a max on the select along with a group by. So it would look something like:
SELECT username, id_info, ...
FROM
(
SELECT a.username, a.id_info, c.packageTitle, MAX(package_id)
FROM users AS a
LEFT JOIN subscribe AS b
ON b.userid = a.userid
LEFT JOIN package AS c
ON b.packageid = c.packageid
GROUP BY a.username, a.id_info, c.packageTitle
)
Remember to list all columns in the select which are also being grouped, except the one on which you are taking the max, or the query will fail.

My-Sql JOIN two tables error

I tried to combine two tables' data.
I got an error like this. can you see why?
Every derived table must have its own alias
SELECT a.title, number
FROM store a
JOIN
( SELECT count(b.code) as number
FROM redeem_codes b
WHERE product = a.title
AND available = "Available")
It's a little hard tell without knowing more about your table structures. I'll give a try anyway:
SELECT a.title, count(b.code) AS number FROM store a
LEFT JOIN redeem_codes b ON b.product = a.title
WHERE b.available = "Available"
GROUP BY a.title;
you need to have ALIAS on your subquery.
SELECT a.title, number 
FROM store a  
JOIN (subquery) b -- b is the `ALIAS`
-- and this query will not give you the result you want
but here's a more efficient query without using subquery,
SELECT a.title, count(b.code) number
FROM store a
INNER JOIN redeem_codes b -- or use LEFT JOIN to show 0
-- for those who have no product
ON b.product = a.title
WHERE b.available = 'Available'
GROUP BY a.title

mysql subquery inside a LEFT JOIN

I have a query that needs the most recent record from a secondary table called tbl_emails_sent.
That table holds all the emails sent to clients. And most clients have several to hundreds of emails recorded. I want to pull a query that displays the most recent.
Example:
SELECT c.name, c.email, e.datesent
FROM `tbl_customers` c
LEFT JOIN `tbl_emails_sent` e ON c.customerid = e.customerid
I'm guessing a LEFT JOIN with a subquery would be used, but I don't delve into subqueries much. Am I going the right direction?
Currently the query above isn't optimized for specifying the most recent record in the table, so I need a little assistance.
It should be like this, you need to have a separate query to get the maximum date (or the latest date) that the email was sent.
SELECT a.*, b.*
FROM tbl_customers a
INNER JOIN tbl_emails_sent b
ON a.customerid = b.customerid
INNER JOIN
(
SELECT customerid, MAX(datesent) maxSent
FROM tbl_emails_sent
GROUP BY customerid
) c ON c.customerid = b.customerid AND
c.maxSent = b.datesent
Would this not work?
SELECT t1.datesent,t1.customerid,t2.email,t2.name
FROM
(SELECT max(datesent) AS datesent,customerid
FROM `tbl_emails_sent`
) as t1
INNER JOIN `tbl_customers` as t2
ON t1.customerid=t2.customerid
Only issue you have then is what if two datesents are the same, what is the deciding factor in which one gets picked?