SQL查询以获取与另一列的MAX值对应的列值?

Posted

技术标签:

【中文标题】SQL查询以获取与另一列的MAX值对应的列值?【英文标题】:SQL Query to get column values that correspond with MAX value of another column? 【发布时间】:2011-10-12 01:54:23 【问题描述】:

好的,这是我的查询:

SELECT
  video_category,
  video_url,
  video_date,
  video_title,
  short_description,
  MAX(video_id) 
FROM
  videos
GROUP BY
  video_category

当它提取数据时,我得到了 video_id 的正确行,但它为其他类别提取了每个类别的第一行。因此,当我得到类别 1 的 video_id 的最大结果时,我得到了最大 ID,但表中的第一行是 url、日期、标题和描述。

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

编辑:已修复。

SELECT
    *
FROM
    videos
WHERE
    video_id IN
    (
        SELECT
            DISTINCT
            MAX(video_id)
        FROM
            videos
        GROUP BY
            video_category
    ) 
ORDER BY
    video_category ASC

【问题讨论】:

为什么需要 DISTINCT? @carillonator 它不是......它实际上是多余的,因为 MAX() 将提供一个独特的结果......显然 【参考方案1】:

我会尝试这样的事情:

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

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

【讨论】:

不确定如何更快,但我会使用它。仍然有效,这就是我所关心的。谢谢。 我在一张类似的桌子上测试过,我的用了 0.02s 你的用了 0.19s :) 这仅适用于每个视频类别只有一个最大 video_id 的情况。假设 OP 会在这些重复项中任意选择,那么更普遍的问题的解决方案是什么? FWIW 这种方法帮助我解决了类似的问题,非常感谢 谢谢你,10 年零几个月后。【参考方案2】:

我最近发布了一种新技术来处理 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

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

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

如果您想检索除 CHAR 以外的类型的值,您可能需要对输出执行额外的 CAST,例如如果您希望 video_date 成为日期时间:

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

如需进一步详细说明此方法与其他旧方法相比的优势,请参阅我的完整博文:https://www.stevenmoseley.com/blog/tech/high-performance-sql-correlated-scalar-aggregate-reduction-queries

【讨论】:

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

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

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;

【讨论】:

【参考方案4】:

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

SELECT
  video_category,
  video_url,
  video_date,
  video_title,
  short_description,
  video_id
FROM
  videos
ORDER BY video_id DESC
LIMIT 1;

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

【讨论】:

【参考方案5】:

选择 video_category、video_url、video_date、video_title、short_description、video_id 来自视频 t1 其中 video_id in (SELECT max(video_id) FROM videos t2 WHERE t1.video_category=t2.video_category );

请提供您的输入输出记录,以便正确理解和测试。

【讨论】:

这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker.

以上是关于SQL查询以获取与另一列的MAX值对应的列值?的主要内容,如果未能解决你的问题,请参考以下文章

如何将一列的列值组合到 MySQL 中的另一列中?

pyspark 将列值与另一列进行比较包含值范围

PHP / SQL - 获取另一列的总和

Mysql 条件 MAX 或 MIN 依赖于另一列值

SQL如何查询出某一列中不同值出现的次数?

基于另一列的每个值的列值总和,然后除以总数