繁体   English   中英

SQL 分组依据:select 值,其中另一列有其最小值/最大值

[英]SQL group by: select value where another column has its min/max

我想按一列分组,同时获取第二列的最小值和最大值,并且(这是棘手的部分。)从第三列获取值,其中第二列在组中具有其最小值。

例子:

我的表:

ID     TS     GRP
==================
 1     20      A
 2     20      B
 3     10      A
 4     30      A
 5     10      B
 6     40      A

期望的结果(ID 应该是 TS 最小值的记录中的值):

ID    MIN_TS   MAX_TS   GRP
============================
 3      10       40      A
 5      10       20      B

一般来说,分组查询很容易:

SELECT <???> AS ID, MIN(TS) AS MIN_TS, MAX(TS) AS MAX_TS, GRP
FROM MyTable
GROUP BY GRP

但是 ID 部分呢? 它不适用于分组,对吗? 但为什么? 最好的解决方法是什么?

在子查询中进行汇总,然后在另一个子查询中查找每个组的ID:

SELECT
  (SELECT TOP(1) id FROM MyTable WHERE grp = agg.grp ORDER BY ts DESC) AS id,
  min_ts, max_ts, grp
FROM (SELECT min(ts) AS min_ts, max(ts) AS max_ts, grp
      FROM MyTable
      GROUP BY grp) agg

或使用窗口功能:

SELECT id, min_ts, max_ts, grp
FROM (SELECT 
        id,
        min(ts) OVER (PARTITION BY grp) min_ts,
        max(ts) OVER (PARTITION BY grp) max_ts,
        grp,
        row_number OVER (PARTITION BY grp ORDER BY ts) rn
      FROM MyTable)
WHERE rn = 1;

该查询使用窗口函数来计算每个组的min_tsmax_ts ,然后过滤以仅包括每个组的第一行(按ts排序)。

有点晚了,但对于未来的人......

我可以提出另一种解决方案,它与现有的有点不同,它的想法基本相同,但它以另一种方式实现(而且它可能更快一点?)。

所以你基本上可以在子查询中进行所有分组和聚合(使用WITH ),然后在该查询和你的原始表之间使用INNER JOIN来获得你想要的东西,它会是这样的......

WITH values AS (
  SELECT
    MIN(ts) as min_ts,
    MAX(ts) AS max_ts,
    grp
  FROM MyTable
  GROUP BY grp
)

SELECT
  tb.id AS id,
  v.min_ts AS min_ts,
  v.max_ts AS max_ts,
  tb.grp AS grp
FROM MyTable tb
INNER JOIN values v ON v.grp = tb.grp AND v.min_ts = tb.ts;

这有点简单,也更直观(至少对我而言)。

我已经在 Postgres DB 上对此进行了测试,并针对以下数据

 id | ts | grp 
----+----+-----
  7 |  5 | A
  3 | 10 | A
  1 | 20 | A
  5 | 30 | A
  4 | 10 | B
  2 | 20 | B
  6 | 30 | B
  8 | 60 | B

它给出了以下结果

 id | min_ts | max_ts | grp 
----+--------+--------+-----
  7 |      5 |     30 | A
  4 |     10 |     60 | B

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM