获取与一列的MAX值对应的其他列?

Dev*_*vin 47 mysql join group-by max

好的,这是我的查询:

SELECT
  video_category,
  video_url,
  video_date,
  video_title,
  short_description,
  MAX(video_id) 
FROM
  videos
GROUP BY
  video_category
Run Code Online (Sandbox Code Playgroud)

当它拉取数据时,我得到video_id的正确行,但它为其他类别拉出第一行.因此,当我获得类别1的video_id的最大结果时,我得到最大ID,但是表格中的第一行是url,date,title和description.

如何让它拉出与最大ID结果相对应的其他列?

编辑:已修复.

SELECT
    *
FROM
    videos
WHERE
    video_id IN
    (
        SELECT
            DISTINCT
            MAX(video_id)
        FROM
            videos
        GROUP BY
            video_category
    ) 
ORDER BY
    video_category ASC
Run Code Online (Sandbox Code Playgroud)

Dal*_*len 39

我会尝试这样的事情:

SELECT
   s.video_id
   ,s.video_category
   ,s.video_url
   ,s.video_date
   ,s.video_title
   ,short_description
FROM videos s
   JOIN (SELECT MAX(video_id) AS id FROM videos GROUP BY video_category) max
      ON s.video_id = max.id
Run Code Online (Sandbox Code Playgroud)

这比你自己的解决方案快得多

  • FWIW这种方法帮助我解决了类似的问题,非常感谢 (3认同)
  • 这仅适用于每个视频类别只有一个max video_id的情况.假设OP会随意选择这些副本,那么更普遍的问题的解决方案是什么? (2认同)

Ste*_*ley 30

我最近发布了一种新技术来处理 MySQL 中的此类问题。

标量减少

Scalar-Aggregate Reduction 是迄今为止实现这一目标的最高性能方法和最简单的方法(用数据库引擎术语),因为它不需要连接、子查询和 CTE。

对于您的查询,它看起来像这样:

SELECT
  video_category,
  MAX(video_id) AS video_id,
  SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_url)), 12) AS video_url,
  SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_date)), 12) AS video_date,
  SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_title)), 12) AS video_title,
  SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), short_description)), 12) AS short_description
FROM
  videos
GROUP BY
  video_category
Run Code Online (Sandbox Code Playgroud)

标量函数和聚合函数的组合执行以下操作:

  1. LPAD 是聚合内相关标识符以允许正确的字符串比较(例如“0009”和“0025”将被正确排序)。假设 INT 主键,我在这里 LPADDING 到 11 个字符。如果您使用 BIGINT,您将需要增加它以支持您的表的序数。如果您在 DATETIME 字段(固定长度)上进行比较,则不需要填充。
  2. 使用输出列连接填充标识符(因此您会得到“00000000009myvalue”与“0000000025othervalue”)
  3. MAX 聚合集,将产生“00000000025othervalue”作为获胜者。
  4. SUBSTRING 结果,这将截断比较的标识符部分,只留下值。

如果要检索 CHAR 以外类型的值,则可能需要对输出执行额外的 CAST,例如,如果您想video_date成为 DATETIME:

CAST(SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_date)), 12) AS DATETIME)

与自联接方法相比,此方法的另一个好处是您可以组合其他聚合数据(不仅仅是最新值),甚至可以组合同一查询中的第一个和最后一个项目,例如

SELECT
    -- Overall totals
    video_category,
    COUNT(1) AS videos_in_category,
    DATEDIFF(MAX(video_date), MIN(video_date)) AS timespan,
    
    -- Last video details
    MAX(video_id) AS last_video_id,
    SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_url)), 12) AS last_video_url,
    ...
    
    -- First video details
    MIN(video_id) AS first_video_id,
    SUBSTRING(MIN(CONCAT(LPAD(video_id, 11, '0'), video_url)), 12) AS first_video_url,
    ...
    
    -- And so on
Run Code Online (Sandbox Code Playgroud)

有关解释此方法与其他旧方法的优点的更多详细信息,我的完整博客文章在这里:https : //www.stevenmoseley.com/blog/tech/high-performance-sql-correlated-scalar-aggregate-reduction-queries

  • 这太棒了,这正是我一直在寻找的。这次真是万分感谢。我期待找到该方法的更多用例。 (4认同)
  • 仅供参考,以 Aurora 中的 100 万行生产表为基准,标量聚合比较的性能比子查询方法好 20%。 (3认同)
  • 我刚刚花了几个小时在 SQL Server 2014 上优化一个类似的查询,并来这里报告可以从该技术中获得的性能改进,但发现你打败了我。就我而言,我正在寻找活动日志中的第一个和最后一个日期以及执行该活动的人。我用来查找第一个操作日期和人员的 T-SQL 表达式是 MIN(ActivityDate) FirstActivityDate 和 SUBSTRING(MIN(CONVERT(VARCHAR, ActivityDate, 21) + ActivityBy), 24, 256) FirstActivityBy。对于 130,000 行,我的性能比 CTE 方法提高了 3 倍。 (2认同)
  • 我们如何将其融入 MySQL 中?我不应该自己做这件事。 (2认同)

Lou*_*Lou 9

一个稍微“质朴”的解决方案,但应该完成相同的工作:

SELECT
  video_category,
  video_url,
  video_date,
  video_title,
  short_description,
  video_id
FROM
  videos
ORDER BY video_id DESC
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)

换句话说,只需生成一个包含所需所有列的表,对其进行排序,使最大值位于顶部,然后将其删除,以便只返回一行。

  • 对我来说是一个很好的选择。简单,优雅,排序和限制都很完美。谢谢! (2认同)

Gui*_*ssé 6

这是一个更通用的解决方案(处理重复项)

CREATE TABLE test(
  i INTEGER,
  c INTEGER,
  v INTEGER
);


insert into test(i, c, v)
values
(3, 1, 1),
(3, 2, 2),
(3, 3, 3),
(4, 2, 4),
(4, 3, 5),
(4, 4, 6),
(5, 3, 7),
(5, 4, 8),
(5, 5, 9),
(6, 4, 10),
(6, 5, 11),
(6, 6, 12);



SELECT t.c, t.v
FROM test t
JOIN (SELECT test.c, max(i) as mi FROM test GROUP BY c) j ON
  t.i = j.mi AND
  t.c  = j.c
ORDER BY c;
Run Code Online (Sandbox Code Playgroud)