第二大 运行 值 SQL

Second greatests running value with SQL

我有一个 table 例如:

n
1 1
2 4
3 4
4 6
5 4
6 8

我可以轻松找到此值的 运行 最大值:

SELECT *, max(value) over (order by n rows unbounded preceding) as mx
FROM table
n mx
1 1 1
2 4 4
3 4 4
4 6 6
5 4 6
6 8 8

如何获得第二大滑动数?所以输出是这样的:

n second_mx
1 1
2 4 1
3 4 4
4 6 4
5 4 4
6 8 6

p.s:

SELECT *,nth_value(value,2) over (order by n rows unbounded preceding) as second_mx
FROM table

不工作,因为order by n描述了如何排序第n个。

您可以使用横向连接:

select t.*, t2.value as second_mx
from t cross join lateral
     (select t2.*
      from t t2
      where t2.n <= t.n
      order by t2.value desc
      offset 1 row fetch first 1 row only
     ) t2;

实际上,您也可以将其表示为关联子查询。

也就是说,这在非小型 table 上不会有很好的性能。

想法是创建一个排序的累积数组并取第二个元素。

PostgreSQL(如果需要第3/4/5个元素可以通过调整数组索引轻松扩展):

SELECT t.*,
     (sort(ARRAY_AGG(t.value) OVER(ORDER BY t.n), 'desc'))[2] AS sec_max
FROM t
ORDER BY n;

db<>fiddle demo

不幸的是,Snowflake 不支持累积 ARRAY_AGG/STRING_AGG。


在使用递归cte构建累积数组的版本以下,然后对数组进行排序并取第二个元素。

数据准备:

CREATE OR REPLACE TABLE t
AS
SELECT 1 AS n, 1 AS value
UNION ALL SELECT 2,4
UNION ALL SELECT 3,4
UNION ALL SELECT 4,6
UNION ALL SELECT 5,4
UNION ALL SELECT 6,8;

辅助函数:

CREATE OR REPLACE FUNCTION array_sort_desc(a array)
  RETURNS array
  LANGUAGE JAVASCRIPT
AS
$$
  return A.sort().reverse();
$$
;

主要查询:

WITH src AS (
   SELECT *, ROW_NUMBER() OVER(ORDER BY n) AS rn FROM t
),cte AS (
  SELECT *,  ARRAY_CONSTRUCT(src.value) AS arr
  FROM src
  WHERE rn=1
  UNION ALL
  SELECT src.*, ARRAY_APPEND(arr, src.value)
  FROM src
  JOIN cte
    ON cte.rn=src.rn-1
)
SELECT cte.n, cte.value, arr, array_sort_desc(arr), array_sort_desc(arr)[1] AS sec_max
FROM cte
ORDER BY n;
/*
+---+-------+-----------------------------------+-----------------------------------+---------+
| N | VALUE |                ARR                |       ARRAY_SORT_DESC(ARR)        | sec_max |
+---+-------+-----------------------------------+-----------------------------------+---------+
| 1 |     1 | [   1  ]                          | [   1  ]                          |         |
| 2 |     4 | [   1,   4  ]                     | [   4,   1  ]                     |       1 |
| 3 |     4 | [   1,   4,   4  ]                | [   4,   4,   1  ]                |       4 |
| 4 |     6 | [   1,   4,   4,   6  ]           | [   6,   4,   4,   1  ]           |       4 |
| 5 |     4 | [   1,   4,   4,   6,   4  ]      | [   6,   4,   4,   4,   1  ]      |       4 |
| 6 |     8 | [   1,   4,   4,   6,   4,   8  ] | [   8,   6,   4,   4,   4,   1  ] |       6 |
+---+-------+-----------------------------------+-----------------------------------+---------+
*/

我提供了一个单独的答案,因为这个答案非常不同(我可能会删除另一个答案)。我相信这只能用 window 函数来处理。我认为这提供了一个解决方案。

这要从一堆解释开始。您可以向下跳过查询 link 到 db<>fiddle.

有两种情况下第二个max真的很简单:

  • 如果当前值是最大值并且之前出现过,那么就是第二个最大值
  • 如果当前值是最大值并且从未出现过,那么前一个最大值就是第二个最大值

一个额外的简单案例:

  • 如果该值小于或等于前一个第二个最大值,则第二个最大值不变。

最后,第二个最大值的重要 属性:

  • 第二个最大值正在增加。

因此,我们的想法是执行以下操作:

  1. 计算“简单”的情况。
  2. 在“简单”情况下分配第二个,当它不会根据简单情况发生变化时。
  3. 在rest中赋当前值。
  4. 计算第二个总和的最大值。

这导致:

select t.*, max(imputed_second_max) over (order by n) as second_max
from (select t.*,
             (case when sometimes_mx_2 is not null then sometimes_mx_2
                   when value <= max(sometimes_mx_2) over (order by n) then max(sometimes_mx_2) over (order by n)
                   else value
              end) as imputed_second_max
      from (select t.*,
                   (case when value = mx and nth_value > 1 then value
                         when value = mx and nth_value = 1 then lag(mx) over (order by n)
                    end) as sometimes_mx_2
            from (select t.*, max(value) over (order by n) as mx,
                         row_number() over (partition by value order by n) as nth_value
                  from t
                 ) t
           ) t
      ) t
order by n;

我发现我需要扩充测试用例以获得更好的覆盖率。我发现递减序列特别棘手。

Here 是一个 db<>fiddle.