【问题标题】:How to select records based on the max value of two fields?如何根据两个字段的最大值选择记录?
【发布时间】:2021-06-30 18:03:17
【问题描述】:

给定以下简单表格:

+-----+-------------+---------+----+
| id_ | match_op_id | version | p1 |
+-----+-------------+---------+----+
|   1 |           1 |       1 |  1 |
|   2 |           1 |       1 |  5 |
|   3 |           1 |       2 |  3 |
|   4 |           1 |       2 |  4 |
|   5 |           2 |       1 |  1 |
|   6 |           2 |       1 |  5 |
|   7 |           2 |       2 |  3 |
|   8 |           2 |       2 |  4 |
|   9 |           2 |       2 |  4 |
+-----+-------------+---------+----+

我想构建一个查询,从最大 version 和最大 p1 中为每个 match_op_id 选择单个记录的 match_op_idp1 字段(不管是哪一个) .所以从上面我会得到输出:

+-------------+----+
| match_op_id | p1 |
+-------------+----+
|           1 |  4 |
|           2 |  4 |
+-------------+----+

在 SO 上的一些帖子之后,我构建了一个查询,用于选择 p1 字段为最大值的所有记录:

SELECT 
    odds_op.match_op_id, odds_op.p1
FROM
    odds_op,
    (SELECT 
        match_op_id, MAX(p1) AS p1
    FROM
        odds_op
    GROUP BY match_op_id) AS max_p1
WHERE
    odds_op.match_op_id = max_p1.match_op_id
        AND odds_op.p1 = max_p1.p1

我现在不知道如何确保我只从最大 version 中选择最大 p1。我认为这可能是一个嵌套的子查询,但我无法弄清楚。我也知道我会在分组方面遇到一些问题,这样我就不会在每个match_op_id 上得到多个记录。任何帮助将不胜感激。

【问题讨论】:

标签: mysql groupwise-maximum


【解决方案1】:

对于 MySql 8.0+,您可以使用 FIRST_VALUE() 窗口函数:

SELECT DISTINCT match_op_id,
       FIRST_VALUE(p1) OVER (PARTITION BY match_op_id ORDER BY version DESC, p1 DESC) p1
FROM odds_op

对于以前的版本,使用NOT EXISTS过滤表,这样只有每个match_op_id中最大version的行被返回,然后聚合得到最大p1

SELECT o1.match_op_id, MAX(o1.p1) p1
FROM odds_op o1
WHERE NOT EXISTS (
  SELECT 1 
  FROM odds_op o2 
  WHERE o2.match_op_id = o1.match_op_id AND o2.version > o1.version
)
GROUP BY o1.match_op_id

或者在WHERE 子句中使用相关子查询:

SELECT o1.match_op_id, MAX(o1.p1) p1
FROM odds_op o1
WHERE o1.version = (SELECT MAX(o2.version) FROM odds_op o2 WHERE o2.match_op_id = o1.match_op_id)
GROUP BY o1.match_op_id

请参阅demo
结果:

match_op_id p1
1 4
2 4

【讨论】:

    【解决方案2】:

    您可以使用GROUP_CONCAT 聚合函数,该函数允许对创建的字符串进行排序并获取第一个元素:

    SELECT match_op_id,  
    SUBSTRING_INDEX(GROUP_CONCAT(p1 ORDER BY version DESC, p1 DESC SEPARATOR '~'),'~',1) AS p1
    FROM odds_op
    GROUP BY match_op_id;
    

    输出(添加 GROUP_CONCAT 作为单独的调试列):

    +--------------+-----+-----------+
    | match_op_id  | p1  |   debug   |
    +--------------+-----+-----------+
    |           1  |  4  | 4~3~5~1   |
    |           2  |  4  | 4~4~3~5~1 |
    +--------------+-----+-----------+
    

    db<>fiddle demo


    如果您使用的是 MariaDB,这会更简单using LIMIT clause

    SELECT match_op_id, 
           GROUP_CONCAT(p1 ORDER BY version DESC, p1 DESC LIMIT 1) AS p1
    FROM odds_op
    GROUP BY match_op_id;
    

    db<>fiddle demo MariaDB

    【讨论】:

      【解决方案3】:

      我不完全确定你想要的结果。

      你说:“我现在不知道如何确保我只从最大版本中选择最大的 p1。”

      这是一种方式:

      SELECT MAX(p1)
      FROM odds_op
      WHERE version = (SELECT MAX(version) FROM odds_op);
      

      如果要选择 match_op_id 和 p1 对,其中 version 是 max version,然后 p1 是 max p1(max p1 where version is max version),请检查此查询:

      SELECT DISTINCT match_op_id, p1
      FROM odds_op
      WHERE version = (SELECT MAX(version) FROM odds_op)
          AND p1 = (SELECT MAX(p1)
                      FROM odds_op
                      WHERE version = (SELECT MAX(version) FROM odds_op));
      

      使用此值尝试不同的响应并检查结果:

      INSERT INTO odds_op (id_, match_op_id, version, p1) VALUES
        ('1', '1', '1', '1'),
        ('2', '1', '1', '5'),
        ('3', '1', '2', '3'),
        ('4', '1', '2', '4'),
        ('5', '2', '1', '1'),
        ('6', '2', '1', '5');
      

      对于我的示例数据,我的查询返回(它不返回对:2,5):

      match_op_id p1
      1 4

      对于您的示例数据,结果与您想要的相同。

      【讨论】:

        【解决方案4】:

        这有点作弊,你必须确定你知道你的 p1 值的上限:

        SELECT match_op_id, MAX(10*version+p1)-MAX(10*version) AS p1
        FROM odds_op
        GROUP BY match_op_id
        

        这适用于小于 9 的 p1s。对于最大 p1 中的每个数字,您需要在查询中为 10s 的两个 10s 添加一个零

        【讨论】:

          【解决方案5】:

          这个脚本在 mysql 上运行。希望这也适用于你。快乐编码!

          SELECT o1.match_op_id, MAX(o1.p1) AS p1
          FROM odds_op o1
          INNER JOIN (
                        SELECT match_op_id, MAX(version) AS version
                        FROM odds_op
                        GROUP BY match_op_id
                     ) AS o2 ON o1.match_op_id = o2.match_op_id AND o1.version = o2.version
          GROUP BY o1.match_op_id
          

          【讨论】:

            猜你喜欢
            • 2021-07-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-08-12
            • 2020-01-14
            相关资源
            最近更新 更多