1

Yeah it seems to be answered multiple times, but everything I tried failed.

The most similar stackoverflow's post is for sure : select rows in sql with latest date for each ID repeated multiple times But the major difference here is that I need to group by after performing some left joins before.

Here is the case :

I have 3 tables (transaction, support, and transaction_support that links the 2 previous tables)

create TABLE `transaction`
(
  id INT,
  date_time DATE,
  notes TEXT,
  PRIMARY KEY (id)
);

create TABLE `support`
(
  id int,
  support_number int ,
  PRIMARY KEY (id)
);

create TABLE `transaction_support`
(
  id INT,
  transaction_id int,
  support_id int,  
  PRIMARY KEY (id), 
  FOREIGN KEY (transaction_id) REFERENCES transaction(id),  
  FOREIGN KEY (support_id) REFERENCES support(id)
);

INSERT INTO `support` values (1, 1111);
INSERT INTO `support` values (2, 2222);
INSERT INTO `support` values (3, 3333);

INSERT INTO `transaction` values (1, '1996-06-28 00:00:00', 'Old data, we shouln''t see it');
INSERT INTO `transaction` values (2, '1996-07-16 00:00:00', 'Old data, we shouln''t see it');
INSERT INTO `transaction` values (3, '2001-04-10 00:00:00', 'Old data, we shouln''t see it');
INSERT INTO `transaction` values (4, '2001-05-14 00:00:00', 'Lastest data from Danny');
INSERT INTO `transaction` values (5, '2001-05-14 00:00:00', 'Lastest data from John');
INSERT INTO `transaction` values (6, '2001-04-10 00:00:00', 'Old data, we shouln''t see it');

INSERT INTO `transaction_support` values (487131, 1, 2);
INSERT INTO `transaction_support` values (488504, 2, 2);
INSERT INTO `transaction_support` values (751126, 3, 2);
INSERT INTO `transaction_support` values (758188, 4, 2);
INSERT INTO `transaction_support` values (4444, 5, 3);
INSERT INTO `transaction_support` values (4445, 6, 3);

Here is a request try :

SELECT s.id AS s_id, t.*, MAX(t.date_time) AS `this date is good`
FROM support AS s
LEFT JOIN transaction_support AS ts ON ts.support_id = s.id
LEFT JOIN transaction AS t ON ts.transaction_id = t.id
GROUP BY ts.support_id

Another try with a sub-query :

SELECT s.id as support_id, t.*, sub.*
FROM support AS s
LEFT JOIN transaction_support AS ts  ON ts.support_id = s.id 
LEFT JOIN transaction AS t ON ts.transaction_id = t.id 
LEFT JOIN (
    SELECT ts.support_id AS `sub_support_id`,
            t.id AS `sub_transaction_id`,
            MAX(t.date_time) AS `sub_last_date`
    FROM transaction_support AS ts 
    LEFT JOIN transaction AS t ON ts.transaction_id = t.id 
    GROUP BY ts.support_id
 ) sub ON ts.support_id = sub.sub_support_id AND t.date_time = sub.sub_last_date
GROUP BY s.id

Expected result would be :

|support_id | transaction_id | transaction_notes       | transaction_date|
|-----------|----------------|-------------------------|-----------------|
| 1         | null           | null                    | null            |
| 2         | 4              | Lastest data from Danny | 2001-05-14      |
| 3         | 5              | Lastest data from John  | 2001-05-14      |

I tried many requests, with and without sub-queries, but so far I never got all latest data from transaction table when I "group by" a support ID.

But I'm pretty sure I need a sub-query...

Here is a fiddle : http://sqlfiddle.com/#!9/adc611/20

Some other similar posts I tried :

If anyone can help me figuring out the solution... thank you ! :)

GMB
  • 216,147
  • 25
  • 84
  • 135
shabang
  • 160
  • 1
  • 13
  • 2
    That's a pretty well-asked question, but missing a critical piece of inforamtion: please show us the result that you want, as tabular text. – GMB Dec 18 '20 at 16:09
  • 1
    Be specific about which version of MySQL you are using. If you are using MySQL 8.0, the best solution involves window functions. But if you use an older version of MySQL, window functions are not supported. – Bill Karwin Dec 18 '20 at 16:13
  • MySQL version is 5.6. I don't know about window functions, I'll read about some, I'm curious – shabang Dec 18 '20 at 16:22
  • @GMB I'll add it soon, thx ! – shabang Dec 18 '20 at 16:23

2 Answers2

1

For your version of MySql there is no simple solution.
You can use NOT EXISTS to get the data for the latest date_time for each support_id and join support to that resultset:

SELECT s.id AS support_id, 
       x.id AS transaction_id,  
       x.notes AS transaction_notes,
       x.date_time AS transaction_date
FROM support AS s
LEFT JOIN (
  SELECT ts.support_id, t.id, t.notes, t.date_time
  FROM transaction_support ts INNER JOIN transaction t
  ON ts.transaction_id = t.id
  WHERE NOT EXISTS (
    SELECT 1
    FROM transaction_support ts2 INNER JOIN transaction t2
    ON ts2.transaction_id = t2.id
    WHERE ts2.support_id = ts.support_id AND t2.date_time > t.date_time
  )
) AS x ON x.support_id = s.id

See the demo.
Results:

> support_id | transaction_id | transaction_notes       | transaction_date
> ---------: | -------------: | :---------------------- | :---------------
>          1 |           null | null                    | null            
>          2 |              4 | Lastest data from Danny | 2001-05-14      
>          3 |              5 | Lastest data from John  | 2001-05-14
forpas
  • 160,666
  • 10
  • 38
  • 76
  • It works, thank you ! I don't understand what's under your NOT EXISTS clause with the "SELECT 1..." but anyway, that's good ! Now I have a problem : @GMB gave me a good answer as well. I don't know enough to decide who's answer is the best one. Do you have an idea ? :D – shabang Dec 18 '20 at 18:20
  • 1
    The requirement in your case is to get the row with the latest date. So NOT EXISTS will return the row of the joined tables (transaction_support and transaction) for which there is not another row with the same support_id and greater date. This is what NOT EXISTS does and it does it without necessarily scanning the whole table. It returns as soon as it finds what it is looking for. Try both answers. – forpas Dec 18 '20 at 18:50
1

If you want the latest transaction per support, one option uses a subquery for filtering in the on clause of the left join:

select s.*, t.*
from support s
left join (
    select t.*, ts.support_id
    from transaction_support ts 
    inner join transaction t 
        on  t.id = ts.transaction_id
        and t.date_time = (
            select max(t1.date_time)
            from transaction_support ts1
            inner join transaction t1 on t1.id = ts1.transaction_id
            where ts1.support_id = ts.support_id
        )
) t on s.id = t.support_id
GMB
  • 216,147
  • 25
  • 84
  • 135
  • It works, thank you ! Indeed it seems there is no easy solution ! Now I have another problem : @forpas gave me a good answer as well. I don't know enough to decide who's answer is the best one. Do you have an idea ? :D (I don't think a can accept 2 answers ?) – shabang Dec 18 '20 at 18:23