第二大 运行 值 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;
不幸的是,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真的很简单:
- 如果当前值是最大值并且之前出现过,那么就是第二个最大值
- 如果当前值是最大值并且从未出现过,那么前一个最大值就是第二个最大值
一个额外的简单案例:
- 如果该值小于或等于前一个第二个最大值,则第二个最大值不变。
最后,第二个最大值的重要 属性:
- 第二个最大值正在增加。
因此,我们的想法是执行以下操作:
- 计算“简单”的情况。
- 在“简单”情况下分配第二个,当它不会根据简单情况发生变化时。
- 在rest中赋当前值。
- 计算第二个总和的最大值。
这导致:
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.
我有一个 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;
不幸的是,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真的很简单:
- 如果当前值是最大值并且之前出现过,那么就是第二个最大值
- 如果当前值是最大值并且从未出现过,那么前一个最大值就是第二个最大值
一个额外的简单案例:
- 如果该值小于或等于前一个第二个最大值,则第二个最大值不变。
最后,第二个最大值的重要 属性:
- 第二个最大值正在增加。
因此,我们的想法是执行以下操作:
- 计算“简单”的情况。
- 在“简单”情况下分配第二个,当它不会根据简单情况发生变化时。
- 在rest中赋当前值。
- 计算第二个总和的最大值。
这导致:
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.