在一些“左连接”之后选择具有最新日期时间字段的完整行

Posted

技术标签:

【中文标题】在一些“左连接”之后选择具有最新日期时间字段的完整行【英文标题】:Select complete row with the latest datetime field after some "left joins" 【发布时间】:2021-03-29 06:58:06 【问题描述】:

是的,它似乎被多次回答,但我尝试的一切都失败了。

最相似的***的帖子肯定是:select rows in sql with latest date for each ID repeated multiple times 但这里的主要区别是我需要在执行一些左连接之后进行分组。

情况是这样的:

我有 3 个表(transaction、support 和 transaction_support 链接前面的 2 个表)

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);

这是一个请求尝试:

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

再试一次子查询:

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

预期结果是:

|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      |

我尝试了许多请求,包括子查询和不包括子查询,但到目前为止,当我“按”支持 ID 进行“分组”时,我从未从事务表中获得所有最新数据。

但我很确定我需要一个子查询...

这是一个小提琴:http://sqlfiddle.com/#!9/adc611/20

我尝试过的其他一些类似的帖子

GROUP BY with MAX(DATE) SQL select only rows with max value on a column mysql select MAX(datetime) not returning max value

如果有人可以帮助我找出解决方案...谢谢! :)

【问题讨论】:

这是一个问得很好的问题,但缺少一个关键信息:请以表格文本的形式向我们展示您想要的结果。 具体说明您使用的 MySQL 版本。如果您使用的是 MySQL 8.0,最好的解决方案是使用窗口函数。但如果你使用的是旧版本的 MySQL,则不支持窗口函数。 MySQL 版本为 5.6。窗口函数我不知道,我会读一些,我很好奇 @GMB 我会尽快添加,谢谢! 【参考方案1】:

如果您想要每个支持的最新事务,一个选项使用子查询在left joinon 子句中进行过滤:

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

【讨论】:

它有效,谢谢!确实,似乎没有简单的解决方案!现在我有另一个问题:@forpas 也给了我一个很好的答案。我不知道谁的答案是最好的。你有想法吗 ? :D (我认为 a 不能接受 2 个答案?)【参考方案2】:

对于您的 MySql 版本,没有简单的解决方案。 您可以使用 NOT EXISTS 来获取每个 support_id 的最新 date_time 的数据,并将 support 加入该结果集:

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

请参阅demo。 结果:

> 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

【讨论】:

成功了,谢谢!我不明白您的 NOT EXISTS 子句中带有“SELECT 1 ...”的内容是什么,但无论如何,这很好!现在我有一个问题:@GMB 也给了我一个很好的答案。我不知道谁的答案是最好的。你有想法吗 ? :D 您的情况的要求是获取最新日期的行。因此 NOT EXISTS 将返回没有另一行具有相同 support_id 和更大日期的连接表(transaction_support 和 transaction)的行。这就是 NOT EXISTS 所做的,它不必扫描整个表就可以做到。一旦找到它正在寻找的东西,它就会返回。尝试两个答案。

以上是关于在一些“左连接”之后选择具有最新日期时间字段的完整行的主要内容,如果未能解决你的问题,请参考以下文章

日期字段上的左连接 + Where 子句

左连接与子查询的性能问题以找出最新日期

如何显示最新交易日期的客户

用于从表中选择具有最新时间戳的行的 JOOQ 代码

在 SQL 查询中选择最新或最近的日期

从日期字段中选择特定数据