【问题标题】:How to select records based on max value of two fields as a subset of third field?如何根据两个字段的最大值选择记录作为第三个字段的子集?
【发布时间】:2021-07-13 22:05:00
【问题描述】:

给定以下简单表格:

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

我想构建一个查询,从每个 @ 的最大 version 中为每个 match_op_id 选择单个记录的 match_op_idbookie_idp1 字段(无关紧要) 987654327@,然后是最大值p1。所以从上面我会得到输出:

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

从我得到的另一个问题的答案来看:

SELECT o1.match_op_id, o1.bookie_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

但是,我不知道如何通过bookie_id 实现最大version。任何帮助将不胜感激。


编辑:

为了解决 cmets 中提出的问题...对于每个 match_op_id,我正在寻找查询以查找最高 version by bookie_id,然后返回最高 p1。所以在上面的例子中 match_op_id "2" 那么bookie_id "1" 的最大 version 是 "1",而 p1 是 "5"。 version 的最大值为 bookie_id“2”为“2”,p1 为“4”。因此查询应该返回bookie_id“1”和“5”的p1

【问题讨论】:

  • SELECT VERSION(); 返回什么?最佳答案取决于您是否可以使用窗口函数来运行排名查询。
  • 嗨@BillKarwin - 我已经添加了版本。我想获取生成的 SQL 并将其转换为 Python 代码,并且很确定我不能在那里使用窗口函数
  • 这不是 1,2,5 和 2,2,4 吗?...
  • 我刚刚添加了一个答案,但我不确定p1 是否必须依赖bookie_id,因为您的问题可能包含不准确之处...
  • @St3an - 抱歉我这周不在。上面的输出是正确的,因为我正在寻找查询以通过bookie_id 查找最高的version,然后返回最高的p1。所以在上面的例子中 match_op_id "2" 那么bookie_id "1" 的最大 version 是 "1",p1 是 "5"。 version 对于bookie_id“2”的最大值是“2”,p1 是“4”。因此查询应该返回bookie_id“1”,p1 为“5”。

标签: mysql mysql-8.0


【解决方案1】:

SQL

WITH max_versions AS
(SELECT match_op_id, bookie_id, MAX(version) AS version
 FROM tbl
 GROUP BY match_op_id, bookie_id),
mv_full AS
(SELECT t.match_op_id, t.bookie_id, t.p1
 FROM max_versions mv
 JOIN tbl t
   ON mv.match_op_id = t.match_op_id
  AND mv.bookie_id = t.bookie_id
  AND mv.version = t.version),
max_p1s AS
(SELECT match_op_id, MAX(p1) AS p1
 FROM mv_full
 GROUP BY match_op_id)
SELECT mf.*
FROM mv_full mf
JOIN max_p1s mp
ON mp.match_op_id = mf.match_op_id
AND mp.p1 = mf.p1

演示

DB Fiddle Demo

【讨论】:

  • 谢谢史蒂夫!根据 cmets,我将通过 SQLAlchemy 将此代码转换为 Python。我看过WITH 声明,它们对我来说有点超出我的设计能力。没有他们有没有办法解决这个问题?
  • @Jossy 是的 - 任何使用 WITH 语句的东西都是可能的,但 CTE 实际上使事情变得更简单并避免重复的子选择。所以会有点不愿意转换,除非有充分的理由(例如,如果使用 8 之前的 MySQL 版本,我猜测不是这种情况,因为 mysql-8.0 被标记)。
  • 谢谢史蒂夫——我想我最好卷起袖子!
  • 如果有帮助,我更新了演示以包含额外的查询(+ cmets),显示每个 CTE 正在做什么。
  • 感谢史蒂夫。我在第一次尝试时设法将它全部融入 Python :-)
【解决方案2】:
SELECT DISTINCT match_op_id, bookie_id, max_version, max_p1
FROM (
SELECT distinct st1.match_op_id match_op_id, st2.bookie_id, st2.max_version, st3.max_p1
        FROM SIMPLE_table st1
        JOIN ( -- version
        SELECT match_op_id, bookie_id, MAX(`version`) max_version FROM simple_table GROUP BY match_op_id, bookie_id) st2
        ON st1.match_op_id = st2.match_op_id
        JOIN ( -- p1
        SELECT match_op_id, bookie_id, `version`, MAX(p1) max_p1 FROM simple_table GROUP BY match_op_id, bookie_id, `version`) st3
        ON st2.max_version=st3.`version` AND st2.bookie_id=st3.bookie_id AND st1.match_op_id=st3.match_op_id) all_
GROUP BY max_version HAVING MAX(max_p1)

给出:

"match_op_id"   "bookie_id" "max_version"   "max_p1"
"2"             "1"         "1"             "5"
"1"             "2"         "2"             "4"

你觉得这对吗?

【讨论】:

    【解决方案3】:

    您可以使用两次ROW_NUMBER()窗口函数。
    首先使用它为match_op_idbookie_id 的每个组合获取最大version 的行,然后对于返回的行,使用它为每个match_op_id 获取最大p1 的行:

    SELECT match_op_id, bookie_id, p1
    FROM (
      SELECT *, ROW_NUMBER() OVER (PARTITION BY match_op_id ORDER BY p1 DESC) rn2
      FROM (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY match_op_id, bookie_id ORDER BY version DESC) rn1
        FROM odds_op
      ) t  
      WHERE rn1 = 1
    ) t
    WHERE rn2 = 1
    

    或者用ROW_NUMBER()FIRST_VALUE

    SELECT DISTINCT match_op_id, 
           FIRST_VALUE(bookie_id) OVER (PARTITION BY match_op_id ORDER BY p1 DESC) bookie_id, 
           MAX(p1) OVER (PARTITION BY match_op_id) p1
    FROM (
      SELECT *, ROW_NUMBER() OVER (PARTITION BY match_op_id, bookie_id ORDER BY version DESC) rn
      FROM odds_op
    ) t  
    WHERE rn = 1
    

    请参阅demo
    结果:

    match_op_id bookie_id p1
    1 2 4
    2 1 5

    【讨论】:

    • 谢谢。根据我希望最终将 SQL 转移到 Python 的 cmets,我认为 SQLAlchemy 不支持窗口函数:-(
    • @Jossy 我不是 SQLAlchemy 专家,但它似乎支持窗口函数:docs.sqlalchemy.org/en/14/core/tutorial.html#window-functions
    • 感谢您的提醒。对于这个问题,我采用了 CTE 方法,但将您的答案标记为绝对有用:-)
    猜你喜欢
    • 2021-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多