【问题标题】:SQL: Greatest row with variable group / conditionSQL:具有可变组/条件的最大行
【发布时间】:2019-02-25 21:56:06
【问题描述】:

我有这个简化的表格记录了一些作业运行:

+----+-----------------------+--------+
| id |          pit          |  stat  |
+----+-----------------------+--------+
|  1 | '2018-09-21 12:00:00' | 'TEST' |
|  1 | '2018-09-20 12:00:00' | 'TEST' |
|  1 | '2018-09-19 12:00:00' | 'PROD' |
|  1 | '2018-09-18 12:00:00' | 'PROD' |
|  1 | '2018-09-17 12:00:00' | 'TEST' |
|    |                       |        |
|  2 | '2018-09-19 14:00:00' | 'TEST' |
|  2 | '2018-09-18 14:00:00' | 'TEST' |
|    |                       |        |
|  3 | '2018-09-19 15:00:00' | 'PROD' |
|  3 | '2018-09-18 15:00:00' | 'PROD' |
|  3 | '2018-09-17 15:00:00' | 'TEST' |
|    |                       |        |
|  4 | '2018-09-21 12:00:00' | 'PROD' |
|  4 | '2018-09-20 12:00:00' | 'PROD' |
+----+-----------------------+--------+

现在我想做以下事情:

对于每个 id:查找最新的作业运行。如果最新运行具有stat = 'PROD',则仅返回该行。如果最新运行具有stat = 'TEST',则返回该行并另外返回具有stat = 'PROD' 的最新运行。

目前我正在运行这个 SQL 以获得最近的 PROD 运行以及每个 id 的最近的 TEST 运行:

SELECT t.*
FROM SOME_TABLE t
INNER JOIN (
    SELECT id, MAX(pit) pit, stat
    FROM SOME_TABLE
    GROUP BY id, stat
) mt ON t.id = mt.id AND t.pit = mt.pit
ORDER BY id asc, pit desc;

SQL Fiddle

现在我缺少的部分是,如果最近有 stat = 'PROD' 运行,我想过滤掉 stat = 'TEST' 运行。

有没有什么好的方法可以在 SQL 中实现这一点?

【问题讨论】:

  • id=2stat 列中没有值 PROD。这个 id 应该返回什么?
  • @OtoShavadze 在这种情况下,应该只返回最近的测试运行。

标签: sql select group-by db2 greatest-n-per-group


【解决方案1】:

有时,重新表述需求会有所帮助。在这里,对于每个 id,您要返回最近的行和最近的“prod”行,这可能是也可能不是同一行。查看问题的另一种方法是按两种方式对行进行排序,并从每个类别中取出第一行:

  1. 按日期降序排列(以获取最新的),每个 ID
  2. 按类型升序(在“测试”行之前获取“产品”行),然后按日期降序,每个 ID:


SELECT id, pit, stat
FROM   (SELECT id, pit, stat
               ROW_NUMBER() OVER (PARTITION BY id ORDER BY pit DESC) AS rn,
               ROW_NUMBER() OVER (PARTITION BY id ORDER BY stat ASC, pit DESC) AS prn
        FROM   some_table) t
WHERE  1 IN (rn, prn)

【讨论】:

  • 非常感谢。重新表述要求使我更清楚。我将在星期一再次工作时尝试一下,看看效果如何。
【解决方案2】:
 with tmp as (
          select SOME_TABLE.* from SOME_TABLE 
          inner join (Select MAX(pit) as pit,
          id from SOME_TABLE group by id) t on t.id = SOME_TABLE.id 
          and t.pit = SOME_TABLE.pit),
    tmp2 as(
          Select MAX(SOME_TABLE.pit) as pit, SOME_TABLE.id, SOME_TABLE.stat 
          from SOME_TABLE 
          inner join tmp on tmp.id = SOME_TABLE.id and tmp.stat = 'TEST'
          where SOME_TABLE.stat = 'PROD'
          group by SOME_TABLE.id, SOME_TABLE.stat
    )
   select * from tmp2
   union 
   select * from tmp
   ORDER BY id, pit desc

【讨论】:

  • 谢谢,我会在下周一检查一下,看看它与其他解决方案相比有多快。由于我正在处理潜在的数百万行,因此我有必要有一个快速的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-17
  • 2017-11-13
相关资源
最近更新 更多