如何根据多行中的最大值选择单行

时间:2012-01-04 07:24:44

标签: mysql sql greatest-n-per-group

  

可能重复:
  SQL: Find the max record per group

我有一个包含四列的表:

name   major    minor  revision
p1     0        4      3
p1     1        0      0
p1     1        1      4
p2     1        1      1
p2     2        5      0
p3     3        4      4

这基本上是包含每个程序版本记录的ca表。我想做一个选择以获取所有程序及其最新版本,因此结果将如下所示:

name   major    minor  revision
p1     1        1      4
p2     2        5      0
p3     3        4      4

我不能只按名称分组并获得每列的最大值,因为那样我最终会得到每列中最高的数字,但不是具有最高版本的特定行。我该怎么设置呢?

7 个答案:

答案 0 :(得分:11)

您可以使用not exists子查询过滤旧记录:

select  *
from    YourTable yt
where   not exists
        (
        select  *
        from    YourTable older
        where   yt.name = older.name and 
                (
                    yt.major < older.major or
                    yt.major = older.major and yt.minor < older.minor or
                    yt.major = older.major and yt.minor = older.minor and
                        yt.revision < older.revision
                )
        )

也可以用MySQL编写:

select  *
from    YourTable yt
where   not exists
        (
        select  *
        from    YourTable older
        where   yt.name = older.name and 
                  (yt.major,    yt.minor,    yt.revision) 
                < (older.major, older.major, older.revision)
        )

答案 1 :(得分:9)

我尝试解决SQL问题的方法是逐步采取措施。

  • 您希望对应于每种产品的最大主要版本的最大次要版本的最大修订版。

每种产品的最大主要编号由:

给出
SELECT Name, MAX(major) AS Major FROM CA GROUP BY Name;

因此,每个产品的最大主要编号对应的最大次要编号为:

SELECT CA.Name, CA.Major, MAX(CA.Minor) AS Minor
  FROM CA
  JOIN (SELECT Name, MAX(Major) AS Major
          FROM CA
         GROUP BY Name
       ) AS CB
    ON CA.Name = CB.Name AND CA.Major = CB.Major
 GROUP BY CA.Name, CA.Major;

因此,最大修订版(对应于每个产品的最大主要编号的最大次要版本号)由下式给出:

SELECT CA.Name, CA.Major, CA.Minor, MAX(CA.Revision) AS Revision
  FROM CA
  JOIN (SELECT CA.Name, CA.Major, MAX(CA.Minor) AS Minor
          FROM CA
          JOIN (SELECT Name, MAX(Major) AS Major
                  FROM CA
                 GROUP BY Name
               ) AS CB
            ON CA.Name = CB.Name AND CA.Major = CB.Major
         GROUP BY CA.Name, CA.Major
       ) AS CC
    ON CA.Name = CC.Name AND CA.Major = CC.Major AND CA.Minor = CC.Minor
 GROUP BY CA.Name, CA.Major, CA.Minor;

经过测试 - 它的工作原理和答案与Andomarquery相同。


性能

我创建了更大量的数据(11616行数据),并运行了Andomar对我的查询的基准时间 - 目标DBMS是在MacOS X 10.7.2上运行的IBM Informix Dynamic Server(IDS)版本11.70.FC2。我使用了Andomar的两个查询中的第一个,因为IDS不支持第二个中的比较符号。我加载了数据,更新了统计数据,然后运行查询,然后是Andomar,然后是Andomar,然后是我的。我还记录了IDS优化器报告的基本成本。来自两个查询的结果数据是相同的(因此查询都是准确的 - 或者同样不准确)。

表未编入索引:

Andomar's query                           Jonathan's query
Time: 22.074129                           Time: 0.085803
Estimated Cost: 2468070                   Estimated Cost: 22673
Estimated # of Rows Returned: 5808        Estimated # of Rows Returned: 132
Temporary Files Required For: Order By    Temporary Files Required For: Group By

具有唯一索引的表(名称,主要,次要,修订):

Andomar's query                           Jonathan's query
Time: 0.768309                            Time: 0.060380
Estimated Cost: 31754                     Estimated Cost: 2329
Estimated # of Rows Returned: 5808        Estimated # of Rows Returned: 139
                                          Temporary Files Required For: Group By

正如您所看到的,该索引显着提高了Andomar查询的性能,但在此系统上它仍然比我的查询更昂贵。该索引为我的查询节省了25%的时间。我很想看到两个版本的Andomar对可比数据量的查询的可比数据,无论是否有索引。 (如果您需要,我可以提供我的测试数据;有132种产品 - 问题中列出的3种产品和129种新产品;每种新产品都有(相同的)90种版本条目。)

出现差异的原因是Andomar查询中的子查询是一个相关的子查询,这是一个相对昂贵的过程(当索引丢失时非常显着)。

答案 2 :(得分:2)

Update3 变量group_concat_max_len的minvalue = 4,因此我们无法使用它。但 你可以:

select 
  name, 
  SUBSTRING_INDEX(group_concat(major order by major desc),',', 1) as major, 
  SUBSTRING_INDEX(group_concat(minor order by major desc, minor desc),',', 1)as minor, 
  SUBSTRING_INDEX(group_concat(revision order by major desc, minor desc, revision desc),',', 1) as revision
from your_table
group by name;

这已经过测试here而且没有,之前的版本没有提供错误的结果,它只有连接值数量的问题。

答案 3 :(得分:2)

SELECT cam.*
FROM 
      ( SELECT DISTINCT name
        FROM ca 
      ) AS cadistinct
  JOIN 
      ca AS cam
    ON ( cam.name, cam.major, cam.minor, cam.revision )
     = ( SELECT name, major, minor, revision
         FROM ca
         WHERE name = cadistinct.name
         ORDER BY major DESC
                , minor DESC
                , revision DESC
         LIMIT 1
       )

这适用于MySQL(当前版本),但我不推荐它:

SELECT *
FROM 
    ( SELECT name, major, minor, revision
      FROM ca
      ORDER BY name
             , major DESC
             , minor DESC
             , revision DESC
    ) AS tmp
GROUP BY name

答案 4 :(得分:1)

如果这些列中有数字,您可以想出某种类型的公式,该公式对于主要的,次要的修订值而言是唯一且有序的。例如。如果数字小于10,你可以将它们作为字符串附加,并比较它们,如:

select name, major, minor, revision, 
       concat(major, minor, revision) as version
from versions

如果它们是不会大于100的数字,您可以执行以下操作:

select name, major, minor, revision, 
       (major * 10000 + minor * 100 + revision) as version
from versions

您可以按名称分组max version,如下所示:

select name, major, minor, revision 
from (
    select name, major, minor, revision, 
           (major * 10000 + minor * 100 + revision) as version
    from versions) v1
where version = (select max (major * 10000 + minor * 100 + revision) 
                 from versions v2 
                 where v1.name = v2.name)

答案 5 :(得分:1)

每个版本号允许最多三位数。如果你想使用更多的数字,那么在主要乘法中加上两个零,每个数字加一个零到一个小的乘法(我希望它很清楚)。

select  t.* 
from yourTable t
join (
    select name, max(major * 1000000 + minor * 1000  + revision) as ver
    from yourTable 
    group by name
) t1 on t1.ver = (t.major * 1000000 + t.minor * 1000  + t.revision)

结果:

name    major   minor   revision
p1      1       1       4
p2      2       5       0
p3      3       4       4

答案 6 :(得分:1)

我是唯一一个认为最好的版本是修订版最高版本的人吗?

所以,

select a.name, a.major, a.minor, a.revision
from table a
where a.revision = (select max(b.revision) from table b where b.name = a.name)