BigQuery:select window 中的第 n 个最小值,按另一个值排序
BigQuery: select the nth smallest value in window, ordered by another value
我的 table 有两个整数列:a
和 b
。对于每一行,我想 select 在具有较小 a
值的行中 b
的第 n 个最小值。这是一个示例 input/output,其中 n=2。
输入:
a | b
-------
1 | 4
2 | 2
3 | 5
4 | 3
5 | 9
6 | 1
7 | 7
8 | 6
9 | 0
输出:
a | 2th min b
-------------
1 | null ← only 1 element in [4], no 2nd min
2 | 4 ← 2nd min between [4,2]
3 | 4 ← 2nd min between [4,2,5]
4 | 3 ← 2nd min between [4,2,5,3]
5 | 3 ← etc.
6 | 2
7 | 2
8 | 2
9 | 1
为了简单起见,我在这里使用了 n=2,但实际上,我想要第 2000 个最小值(或其他一些较大的常量)。可以假定列 a
包含不同的整数(甚至 1、2、3 ……如果这样更容易的话)。
问题是,如果我在 window 子句中使用 ORDER BY b
和 NTH_VALUE
,它只会根据错误的值集计算答案:
WITH data AS (
SELECT 1 AS a, 4 AS b
UNION ALL SELECT 2 AS a, 2 AS b
UNION ALL SELECT 3 AS a, 5 AS b
UNION ALL SELECT 4 AS a, 3 AS b
UNION ALL SELECT 5 AS a, 9 AS b
UNION ALL SELECT 6 AS a, 1 AS b
)
SELECT nth_value(b, 2) over (order by a)
from data
returns [null, 2, 2, 2, 2, 2]
:值按 a
排序(因此与它们出现的顺序相同),因此值 b=2
始终是第二个地方。我想按 a 排序,然后 然后 取 b 的第 n 个最小值。知道如何在 BigQuery 中编写这个(最好是标准 SQL)吗?
以下适用于 BigQuery Standard SQL,并为给定的示例生成正确的结果。
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 a, 4 b UNION ALL
SELECT 2, 2 UNION ALL
SELECT 3, 5 UNION ALL
SELECT 4, 3 UNION ALL
SELECT 5, 9 UNION ALL
SELECT 6, 1 UNION ALL
SELECT 7, 7 UNION ALL
SELECT 8, 6 UNION ALL
SELECT 9, 0
)
SELECT
a,
(SELECT b FROM
(SELECT b FROM UNNEST(c) b ORDER BY b LIMIT 2)
ORDER BY b DESC LIMIT 1
) b2
FROM (
SELECT a, IF(ARRAY_LENGTH(c) > 1, c, [NULL]) c
FROM (
SELECT a, ARRAY_AGG(b) OVER (ORDER BY a) c
FROM `project.dataset.table`
)
)
-- ORDER BY a
预期结果如下
Row a b2
1 1 null
2 2 4
3 3 4
4 4 3
5 5 3
6 6 2
7 7 2
8 8 2
9 9 1
注意:要使其适用于第 2000 个元素,您可以在 LIMIT 2
中将 2 更改为 2000
同时,我承认它对我来说看起来有点 ugly/messy 并且不确定可扩展性,但您可以试一试
Quick Update
下面是看起来不那么丑的版本(当然输出相同)
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 a, 4 b UNION ALL
SELECT 2, 2 UNION ALL
SELECT 3, 5 UNION ALL
SELECT 4, 3 UNION ALL
SELECT 5, 9 UNION ALL
SELECT 6, 1 UNION ALL
SELECT 7, 7 UNION ALL
SELECT 8, 6 UNION ALL
SELECT 9, 0
)
SELECT a, c[SAFE_ORDINAL(2)] b2 FROM (
SELECT x.a, ARRAY_AGG(y.b ORDER BY y.b LIMIT 2) c
FROM `project.dataset.table` x
CROSS JOIN `project.dataset.table` y
WHERE y.a <= x.a
GROUP BY x.a
)
-- ORDER BY a
对于第 2000 个元素,将 LIMIT 2
和 SAFE_ORDINAL(2)
中的 2
替换为 2000
由于(现在)明确的 CROSS JOIN
,可伸缩性仍然可能存在相同的问题
我的 table 有两个整数列:a
和 b
。对于每一行,我想 select 在具有较小 a
值的行中 b
的第 n 个最小值。这是一个示例 input/output,其中 n=2。
输入:
a | b
-------
1 | 4
2 | 2
3 | 5
4 | 3
5 | 9
6 | 1
7 | 7
8 | 6
9 | 0
输出:
a | 2th min b
-------------
1 | null ← only 1 element in [4], no 2nd min
2 | 4 ← 2nd min between [4,2]
3 | 4 ← 2nd min between [4,2,5]
4 | 3 ← 2nd min between [4,2,5,3]
5 | 3 ← etc.
6 | 2
7 | 2
8 | 2
9 | 1
为了简单起见,我在这里使用了 n=2,但实际上,我想要第 2000 个最小值(或其他一些较大的常量)。可以假定列 a
包含不同的整数(甚至 1、2、3 ……如果这样更容易的话)。
问题是,如果我在 window 子句中使用 ORDER BY b
和 NTH_VALUE
,它只会根据错误的值集计算答案:
WITH data AS (
SELECT 1 AS a, 4 AS b
UNION ALL SELECT 2 AS a, 2 AS b
UNION ALL SELECT 3 AS a, 5 AS b
UNION ALL SELECT 4 AS a, 3 AS b
UNION ALL SELECT 5 AS a, 9 AS b
UNION ALL SELECT 6 AS a, 1 AS b
)
SELECT nth_value(b, 2) over (order by a)
from data
returns [null, 2, 2, 2, 2, 2]
:值按 a
排序(因此与它们出现的顺序相同),因此值 b=2
始终是第二个地方。我想按 a 排序,然后 然后 取 b 的第 n 个最小值。知道如何在 BigQuery 中编写这个(最好是标准 SQL)吗?
以下适用于 BigQuery Standard SQL,并为给定的示例生成正确的结果。
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 a, 4 b UNION ALL
SELECT 2, 2 UNION ALL
SELECT 3, 5 UNION ALL
SELECT 4, 3 UNION ALL
SELECT 5, 9 UNION ALL
SELECT 6, 1 UNION ALL
SELECT 7, 7 UNION ALL
SELECT 8, 6 UNION ALL
SELECT 9, 0
)
SELECT
a,
(SELECT b FROM
(SELECT b FROM UNNEST(c) b ORDER BY b LIMIT 2)
ORDER BY b DESC LIMIT 1
) b2
FROM (
SELECT a, IF(ARRAY_LENGTH(c) > 1, c, [NULL]) c
FROM (
SELECT a, ARRAY_AGG(b) OVER (ORDER BY a) c
FROM `project.dataset.table`
)
)
-- ORDER BY a
预期结果如下
Row a b2
1 1 null
2 2 4
3 3 4
4 4 3
5 5 3
6 6 2
7 7 2
8 8 2
9 9 1
注意:要使其适用于第 2000 个元素,您可以在 LIMIT 2
同时,我承认它对我来说看起来有点 ugly/messy 并且不确定可扩展性,但您可以试一试
Quick Update
下面是看起来不那么丑的版本(当然输出相同)
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 a, 4 b UNION ALL
SELECT 2, 2 UNION ALL
SELECT 3, 5 UNION ALL
SELECT 4, 3 UNION ALL
SELECT 5, 9 UNION ALL
SELECT 6, 1 UNION ALL
SELECT 7, 7 UNION ALL
SELECT 8, 6 UNION ALL
SELECT 9, 0
)
SELECT a, c[SAFE_ORDINAL(2)] b2 FROM (
SELECT x.a, ARRAY_AGG(y.b ORDER BY y.b LIMIT 2) c
FROM `project.dataset.table` x
CROSS JOIN `project.dataset.table` y
WHERE y.a <= x.a
GROUP BY x.a
)
-- ORDER BY a
对于第 2000 个元素,将 LIMIT 2
和 SAFE_ORDINAL(2)
中的 2
替换为 2000
由于(现在)明确的 CROSS JOIN