TABLE: LOAN
Loan_no Amount SSS_no Loan_date
7 700.00 0104849222 2010-01-03
8
Try this instead:
SELECT l1.*
FROM loan AS l1
INNER JOIN
(
SELECT SSS_no, MAX(Loan_date) LatestDate
FROM loan
GROUP BY SSS_no
) AS l2 ON l1.SSS_no = l2.SSS_no
AND l1.loan_date = l2.LatestDate;
This will give you:
| LOAN_NO | AMOUNT | SSS_NO | LOAN_DATE |
----------------------------------------------
| 16 | 230 | 104849222 | 2013-03-06 |
| 17 | 265 | 119611199 | 2011-04-30 |
| 18 | 455 | 3317131410 | 2013-03-10 |
The reason why you are getting unexpected results is because you are using a GROUP BY
on only one column in the SELECT
list and you are not using any aggregate functions on all the columns.
MySQL uses an extension to the GROUP BY
function which can cause unexpected results when you do not GROUP BY
or aggregate all items in the SELECT
list. (see MySQL Extensions to GROUP BY)
From the MySQL Docs:
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. ... You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Sorting of the result set occurs after values have been chosen, and ORDER BY does not affect which values the server chooses.
The only way that you can be sure to return the correct result is to alter your query to aggregate and GROUP BY
properly.
So you can use something similar to this:
select l1.loan_no,
l1.amount,
l1.SSS_no,
l1.loan_date
from loan l1
inner join
(
select SSS_no, max(loan_date) Loan_date
from loan
group by SSS_no
) l2
on l1.SSS_no = l2.SSS_no
and l1.loan_date = l2.loan_date
See SQL Fiddle with Demo
This implements a subquery to get the max(loan_date)
for each SSS_no
. This subquery is then joined back to your table on both the SSS_no
and the max loan_date
which will guarantee that you get the correct result for each SSS_no
.
The MySQL reference suggests several ways to solve this. The simplest is a subquery:
SELECT *
FROM loan l1
WHERE loan_date=(SELECT MAX(l2.loan_date)
FROM loan l2
WHERE l1.sss_no = l2.sss_no);
Given that this type of subqueries potentially have bad performance, they also suggest using a JOIN
(essentially Mahmoud Gamal's answer):
SELECT l1.loan_no, l1.amount, l1.sss_no, l1.loan_date
FROM loan l1
JOIN (
SELECT loan_no, MAX(loan_date) AS loan_date
FROM loan
GROUP BY sss_no) AS l2
ON l1.loan_date = l2.loan_date AND l1.sss_no = l2.sss_no;
A third option is:
SELECT l1.loan_no, l1.amount, l1.sss_no, l1.loan_date
FROM loan l1
LEFT JOIN loan l2 ON l1.sss_no = l2.sss_no AND l1.loan_date < l2.loan_date
WHERE l2.sss_no IS NULL;
The LEFT JOIN
works on the basis that when l1.loan_date
is at its maximum value, there is later l2.loan_date
, so the l2 row values will be NULL.
All these should have the same output, but likely differ in performance.