简体   繁体   English

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

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

I want to group by one column, get both min and max of a second column, and (this is the tricky part.) get the value from a third column where the second column has its min value in the group.我想按一列分组,同时获取第二列的最小值和最大值,并且(这是棘手的部分。)从第三列获取值,其中第二列在组中具有其最小值。

Example:例子:

MyTable:我的表:

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

Desired result (ID should be the value from the record where TS has its minimum):期望的结果(ID 应该是 TS 最小值的记录中的值):

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

In general, the grouping query is very easy:一般来说,分组查询很容易:

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

But what about the ID part?但是 ID 部分呢? It doesn't work this way with grouping, right?它不适用于分组,对吗? But why?但为什么? And what's the best workaround?最好的解决方法是什么?

Do the aggregation in a subquery, then look up the ID for each group in another subquery: 在子查询中进行汇总,然后在另一个子查询中查找每个组的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

Or use window functions: 或使用窗口功能:

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;

This query uses window functions to calculate min_ts and max_ts for each group, and then filters to only include the first row for each group (ordered by ts ). 该查询使用窗口函数来计算每个组的min_tsmax_ts ,然后过滤以仅包括每个组的第一行(按ts排序)。

A bit late, but for future comers...有点晚了,但对于未来的人......

I can propose another solution that's a bit different than the existing one, it's basically the same idea, but it's implemented in another way (and it's maybe a bit faster?).我可以提出另一种解决方案,它与现有的有点不同,它的想法基本相同,但它以另一种方式实现(而且它可能更快一点?)。

So you can basically make all the grouping and aggregation in a sub query (using WITH ), then use INNER JOIN between that query and your original table to get what you want, it would be something like this...所以你基本上可以在子查询中进行所有分组和聚合(使用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;

This is a bit simpler, and a bit more intuitive (at least to me).这有点简单,也更直观(至少对我而言)。

I have tested this on Postgres DB, and for the following data我已经在 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

It gives the following results它给出了以下结果

 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