【问题标题】:MySql select statement get most recent row for each client_idMySql 选择语句获取每个 client_id 的最新行
【发布时间】:2012-07-05 10:48:43
【问题描述】:

您好,我的 mysql 选择语句有问题,我无法理解,

表client_directory_data

id 整数, 已验证的 int, client_id 整数, 创建时间戳, 描述长文本

select * from client_directory_data where verify = 1 order by created desc

但这会为每个 client_id 选择多行

我需要做的是选择每个 client_id 具有验证 = 1 但只获取每个 client_id 的最新行,我希望这是有道理的。

【问题讨论】:

  • 不确定此页面上发生了什么,但实际上有人通过指向 sql fiddle sqlfiddle.com/#!2/7cc95/1的链接正确回答了这个问题@
  • 无论是谁都值得打勾作为正确答案,因为它有效,从 client_directory_data 中选择 * 其中已验证 = 1 group by client_id 具有 max(created)
  • 这个 sqlfiddle 错误:client_id = 1 的 ID 应该是 2,而不是 1...
  • 对我的查询尝试相同的 sqlfiddle ;-)

标签: mysql database select


【解决方案1】:

这是我一直面临的问题。幸运的是,这样做有一个不错的小技巧:

SELECT
client_id,
SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY created DESC),",",1) AS `id`
FROM client_directory_data
WHERE verified = 1
GROUP BY client_id

如果你想要整行,你可以像这样加入它:

SELECT
*
FROM (
  SELECT
  client_id,
  SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY created DESC),",",1) AS `id`
  FROM client_directory_data
  WHERE verified = 1
  GROUP BY client_id
) ids
JOIN client_directory_data USING (id);

当然,如果您无论如何都按索引字段排序(因此无论如何您都可以有效地加入),最好使用MAX(id) AS id,尽管它实际上对性能影响很小。使用 MAX() 的主要原因实际上是为了让代码更简单一些。它还避免了如果字段包含逗号(您可以使用组 concat 的不同分隔符来解决)或达到最大 GROUP_CONCAT 长度(可以使用SET group_concat_max_len = xxx; 扩展并且无论如何只会导致警告)时可能遇到的陷阱。

我可以理解为什么这在直觉上看起来会出现性能问题,但它实际上是我为这些查询找到的最佳性能方法 - 特别是在大型表上。

以下是我从当前可用的一些较大的表格中获取的一些基准,比较此线程中的三种方法。

查询 A:(~5,000 条记录,~900 个结果,非索引字段)

  • GROUP_CONCAT 方法:0.0100 秒
  • MAX 方法:0.102 秒
  • LEFT JOIN 方法:0.0082 秒

查询 B:(~300,000 条记录,~95,000 个结果)

  • GROUP_CONCAT 方法:1.8618 秒
  • MAX 方法:1.7904 秒
  • LEFT JOIN 方法:6.4649 秒

查询 C:(~300,000 条记录,~7 个结果)

  • GROUP_CONCAT 方法:0.103 秒
  • MAX 方法:0.0102 秒
  • LEFT JOIN 方法:(4小时后我觉得无聊)

查询 D:(约 500,000 条记录,约 5,000 个被分组字段的不同值)

  • GROUP 方法:0.1355 秒
  • MAX 方法:0.0429 秒
  • LEFT JOIN 方法:(10分钟后我就无聊了)

【讨论】:

  • 不错的技巧,但只给出 id,而不是整行。至此,写SELECT client_id, MAX(id) FROM client_directory_data WHERE verified = 1 GROUP BY client_id;就大同小异了。
  • 仅当具有 MAX id 的行与创建的 MAX 相同时,它可能是,但这取决于系统。一旦你有了这一行,加入它就很容易了,但我想我可能实际上应该在我的答案中包含它。 编辑...
  • 这是一个很好的答案,但我会继续为我的骨头而战,说我的查询在需要性能的地方确实更好,因为它不涉及子查询。
  • 我实际上曾经按照您的方式进行操作,但是当我在处理约 100 万个记录表时进行大量基准测试时,这种方式会更好。这可能取决于您存储的数据类型,但根据我的经验,连接到主键的子查询实际上比像这样将表连接到自身要高效得多。不过,我很想知道哪种方法最适合 OP。
  • GROUP_CONCAT() 用于此类查询(“greatest-n-per-group”)并不好,原因有很多。不是因为子查询。
【解决方案2】:

这是有道理的,是一个经典的问题。

假设the most recent row is the one with highest id,你可以使用:

SELECT *
FROM client_directory_data c
LEFT JOIN client_directory_data d ON c.client_id = d.client_id AND d.verified = 1 AND d.id > c.id
WHERE d.id IS NULL
    AND c.verified = 1;

你可以有这个查询模式here的解释。

【讨论】:

  • 感谢 Olivier,但这只是拉一个 client_id 并且有很多 client_id 行,请有其他想法
  • 我非常肯定这种模式正在发挥作用。您真的确定您在具有多个 client_id 值且对应验证 = 1 的数据集上对其进行了测试吗?
  • 试试我对问题评论中给出的 sqlfiddle 的查询,你仍然只有 1 行吗?
  • 您当然可以将 d.id > c.id 更改为 d.created > c.created 以删除 ID 假设?另外,我认为您需要 d.verified = 1 在 ON 子句中,否则它只会返回最后一个条目已验证的 client_id
  • @Braiba 你对这两个 cmets 都是 100% 正确的,这为你赢得了 +1 :)
【解决方案3】:

将 id 作为表 client_directory_data 的主键

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-24
    • 1970-01-01
    • 2011-07-12
    相关资源
    最近更新 更多