SQL - 使用 GROUP BY 获取子查询子集中或连接中的最新记录

Posted

技术标签:

【中文标题】SQL - 使用 GROUP BY 获取子查询子集中或连接中的最新记录【英文标题】:SQL - obtain latest record in subquery subset or in join using GROUP BY 【发布时间】:2014-05-12 04:41:23 【问题描述】:

我已经为此困惑了一段时间,并意识到是时候寻求帮助了。作为与第三方系统集成的一部分,我在现有系统中引入了一个附加表来管理记录的同步。我将缩小表结构,只包含足够的细节来呈现问题:

Table 1: data
Columns: (int)data_id*, (varchar)name, (datetime)date_created

Table 2: sync
Columns: (int)sync_id*, (int)data_id, (int)result, (varchar)details,
         (datetime)date_created

* denotes primary index

当第三方系统尝试同步记录时,它使用 HTTP GET 请求,服务器端脚本返回一个 XML 响应,其中包含第一次等待同步的记录信息(不存在同步记录此 data_id ),并且还记录等待重试同步的信息,但前一次尝试失败(此 data_id 的最新同步记录的结果值为 0)。然后,第三方系统使用 HTTP POST 请求到不同的服务器端脚本,该脚本提供有关它能够匹配和成功同步的记录 (result = 1) 以及它无法匹配和同步的记录的反馈 ( result = 0, details = "Error Message")。

每个数据记录经常会有多个同步记录,因为有许多有效的场景可能会阻止用户在没有先采取某些操作的情况下成功同步。

记录每次同步尝试很重要,因此仅向数据表添加同步列是不可接受的。

我尝试使用 SQL 的伪代码版本是这样的:

    从数据表和每条记录旁边获取所有记录。 对于这些记录中的每一个,通过匹配 data_id 找到最新的同步记录,按降序排列同步记录(最新的在顶部),并将同步记录限制为只有 1 条(我们只需要最近的同步记录查询)。 显示数据和最新同步记录的列。如果不存在同步记录,则仍应显示数据记录,并且同步列只需填充 NULL 值。

到目前为止,我使用的最接近的 SQL 语句使用 LEFT JOIN

SELECT d.data_id, d.name, d.date_created, s.sync_id, s.result, s.details
FROM (
    SELECT data_id, name, date_created
    FROM data
) AS d 
LEFT JOIN (
    SELECT sync_id, data_id, result, details, date_created
    FROM sync 
    GROUP BY data_id
) AS s 
ON d.data_id = s.data_id 
ORDER BY d.date_created DESC;

不幸的是,这似乎没有获取最新的同步记录,但GROUP BY data_id 似乎只是获取了它找到的第一个同步记录。 mysql 语法不允许我在 GROUP BY data_id 行之前放置 ORDER BY date_created DESC。如果我将此 ORDER BY 语句放在 GROUP BY 行之后,它似乎不起作用,并且最近的同步记录不是结果中数据列旁边显示的记录。

我开始使用的一个更简单的版本具有完全相同的问题,即不一定要获取最新的同步记录:

SELECT d.data_id, d.name, d.date_created, s.result, s.details 
FROM data AS d LEFT JOIN sync AS s ON d.data_id = s.data_id 
WHERE s.result = 0 OR s.result IS NULL;

我也尝试过使用子查询来实现这一点,同样的问题:

SELECT d.data_id, d.name, d.date_created, s.sync_id, s.result, s.details
FROM (
    SELECT data_id, name, date_created
    FROM data
) AS d, (
    SELECT s.sync_id, s.data_id, s.result, s.details, s.date_created
    FROM sync AS s, data AS d
    WHERE s.data_id = d.data_id
    ORDER BY s.date_created DESC
) AS s 
WHERE d.data_id = s.data_id 
ORDER BY s.date_created DESC;

请有人建议我如何确定在单个查询中仅获取最新的同步记录以及数据记录。我很高兴该解决方案根据需要包含连接或子查询的任何组合。谢谢。

【问题讨论】:

【参考方案1】:

有几种方法可以做到这一点。这是一个聚合sync 表以获取每个data_id 记录的最新date_created 的方法:

SELECT d.data_id, d.name, d.date_created, s.result, s.details 
FROM data AS d LEFT JOIN
     sync AS s
     ON d.data_id = s.data_id LEFT JOIN
     (select s.data_id, max(date_created) as maxdc
      from sync s
      group by s.data_id
     ) smax
     on s.data_id = smax.data_id and s.date_created = smax.maxdc
WHERE s.result = 0 OR s.result IS NULL;

【讨论】:

嗨,戈登,这似乎为每个同步记录返回一行。例如,如果我将最后一行的 WHERE 子句替换为 WHERE d.data_id = 1367,它将返回 3 行而不是仅包含最新同步记录信息的单行。 认为连接可能应该重新排序,即将smax 连接到数据,然后将sync 连接到smax,因为它只是 smax 的左连接,它实际上并没有过滤结果全部。仍然 +1,因为这是正确的方法,并且不依赖 MySQL 的扩展组。【参考方案2】:

您只需要在查询中对同步表使用 order by 然后 group by,因为 mysql 不支持 order by before group by,因为 mysql 使用第一个 group by 然后 order by。

所以你可以使用下面的查询。

SELECT d.data_id, d.name, d.date_created, s.sync_id, s.result, s.details
FROM `data` AS d LEFT JOIN (
  SELECT sync_id, data_id, result, details, date_created FROM (
    SELECT sync_id, data_id, result, details, date_created
    FROM sync
    ORDER BY date_created DESC
  ) a GROUP BY a.data_id
) s
ON d.data_id = s.data_id 
ORDER BY d.date_created DESC;

【讨论】:

感谢 Zafar,这实际上似乎解决了这个问题,虽然我花了一点时间才意识到您将 ORDER BY 子查询包装在 GROUP BY 子查询中以实现此目的.干得好-谢谢。 请注意,MySQL 文档明确警告不要使用 group by 的此扩展来从特定行 (dev.mysql.com/doc/refman/5.7/en/group-by-extensions.html) 获取列。虽然它可能在实践中起作用(有时?),但它在理论上不起作用。

以上是关于SQL - 使用 GROUP BY 获取子查询子集中或连接中的最新记录的主要内容,如果未能解决你的问题,请参考以下文章

SQL 不能使用从 group by 中的子查询返回的列

如何在没有算术计数的SQL子查询中使用GROUP BY

具有 group by 和两个表连接的 Sql 子查询

如何在没有附加查询的情况下在 SQL Server 中连接没有子查询的 GROUP BY 子句中的字符串?

sql语句执行顺序之group by、order by

如何在 Laravel 8 中使用 PostgreSQL 中的子查询通过 group by 子句获取行值?