Google bigquery 在BigQuery中将行分组到固定大小的数组中

Google bigquery 在BigQuery中将行分组到固定大小的数组中,google-bigquery,Google Bigquery,我想将BigQuery中的数据聚合到固定大小的数组中,以便在其他地方分页。所以,从这样的事情开始: +-------+ | num | +-------+ | one | | two | | three | | four | | five | | six | | seven | | eight | | nine | | ten | +-------+ 如果页面大小为5,我想以这样的方式结束: +------+------------------------------

我想将BigQuery中的数据聚合到固定大小的数组中,以便在其他地方分页。所以,从这样的事情开始:

+-------+
|  num  |
+-------+
| one   |
| two   |
| three |
| four  |
| five  |
| six   |
| seven |
| eight |
| nine  |
| ten   |
+-------+
如果页面大小为5,我想以这样的方式结束:

+------+--------------------------------------+
| page |                 nums                 |
+------+--------------------------------------+
|    0 | ["one","two","three","four","five"]  |
|    1 | ["six","seven","eight","nine","ten"] |
+------+--------------------------------------+
我目前正在使用ROW_编号进行此操作。下面是一个完整的代码示例,从上面的10行字符串开始,并创建所需的输出:

bq query "#StandardSQL
-- Create dummy input
WITH input AS (
      SELECT * FROM UNNEST((
         SELECT SPLIT('one,two,three,four,five,six,seven,eight,nine,ten', ',')
      )) as num
)
-- Paginate it
SELECT page, ARRAY_AGG(num) as nums FROM (
  SELECT *, CAST(FLOOR(row / 5) AS INT64) as page FROM (
    SELECT *, (ROW_NUMBER() OVER (PARTITION BY 1) - 1) as row FROM input
  )
) GROUP BY page"

+------+--------------------------------------+
| page |                 nums                 |
+------+--------------------------------------+
|    0 | ["one","two","three","four","five"]  |
|    1 | ["six","seven","eight","nine","ten"] |
+------+--------------------------------------+
在输入数据中大约有3000万行之前,这种方法一直运行良好,然后我发现在查询执行期间超出了可怕的资源:查询无法在分配的内存中执行。错误,因为BQ正在尝试计算内存中所有3000多万行的行号

有什么替代方法可以产生与我的示例相同的输出,但可以扩展到超过1亿行而不中断?

选项1:

#StandardSQL
WITH input AS (
  SELECT x, CONCAT('item_',CAST(y AS STRING), '_', CAST(x AS STRING)) item
  FROM UNNEST(GENERATE_ARRAY(1, 1000000)) x,
    UNNEST(GENERATE_ARRAY(1, 100)) y
), temp AS (
  SELECT STRING_AGG(item) line, CAST(100 * RAND() - 0.5 AS INT64) grp
  FROM input
  GROUP BY grp
)
SELECT grp, page, items FROM temp, 
UNNEST(REGEXP_EXTRACT_ALL(line, r'(?:[^,]+,){4}[^,]+|(?:[^,]+,)*[^,]+')) items WITH OFFSET page; 
它生成的页面大小分布明显低于页面大小分布。由于使用RAND,这可能略有不同:

正如您在上面所看到的,输入只是模拟了一个有100M行的表,其余的代码生成如下内容

选项2:解决那些不完整的页面

Row page-size   pages    
2   3           29   
3   2           21   
4   4           18   
5   1           5    
下面的脚本可以使用

#StandardSQL
CREATE TEMP TABLE temp_result AS 
WITH input AS (
  SELECT x, CONCAT('item_',CAST(y AS STRING), '_', CAST(x AS STRING)) item
  FROM UNNEST(GENERATE_ARRAY(1, 1000000)) x,
    UNNEST(GENERATE_ARRAY(1, 100)) y
), temp AS (
  SELECT STRING_AGG(item) line, CAST(100 * RAND() - 0.5 AS INT64) grp
  FROM input
  GROUP BY grp
)
SELECT grp, page, x FROM temp, 
UNNEST(REGEXP_EXTRACT_ALL(line, r'(?:[^,]+,){4}[^,]+|(?:[^,]+,)*[^,]+')) x WITH OFFSET page;

SELECT * FROM temp_result WHERE ARRAY_LENGTH(SPLIT(x)) = 5 UNION ALL
SELECT -1, page, x FROM (
  SELECT STRING_AGG(x) line FROM temp_result WHERE ARRAY_LENGTH(SPLIT(x)) < 5
), UNNEST(REGEXP_EXTRACT_ALL(line, r'(?:[^,]+,){4}[^,]+|(?:[^,]+,)*[^,]+')) x WITH OFFSET page
其中产生以下内容,至少有无-非完整页面-2000万页,每个页面包含5个项目

注意:我知道这很可能不是OP所期望的,但这是我所能做到的最好的,它在100米行上运行时不会出错

选项1:

#StandardSQL
WITH input AS (
  SELECT x, CONCAT('item_',CAST(y AS STRING), '_', CAST(x AS STRING)) item
  FROM UNNEST(GENERATE_ARRAY(1, 1000000)) x,
    UNNEST(GENERATE_ARRAY(1, 100)) y
), temp AS (
  SELECT STRING_AGG(item) line, CAST(100 * RAND() - 0.5 AS INT64) grp
  FROM input
  GROUP BY grp
)
SELECT grp, page, items FROM temp, 
UNNEST(REGEXP_EXTRACT_ALL(line, r'(?:[^,]+,){4}[^,]+|(?:[^,]+,)*[^,]+')) items WITH OFFSET page; 
它生成的页面大小分布明显低于页面大小分布。由于使用RAND,这可能略有不同:

正如您在上面所看到的,输入只是模拟了一个有100M行的表,其余的代码生成如下内容

选项2:解决那些不完整的页面

Row page-size   pages    
2   3           29   
3   2           21   
4   4           18   
5   1           5    
下面的脚本可以使用

#StandardSQL
CREATE TEMP TABLE temp_result AS 
WITH input AS (
  SELECT x, CONCAT('item_',CAST(y AS STRING), '_', CAST(x AS STRING)) item
  FROM UNNEST(GENERATE_ARRAY(1, 1000000)) x,
    UNNEST(GENERATE_ARRAY(1, 100)) y
), temp AS (
  SELECT STRING_AGG(item) line, CAST(100 * RAND() - 0.5 AS INT64) grp
  FROM input
  GROUP BY grp
)
SELECT grp, page, x FROM temp, 
UNNEST(REGEXP_EXTRACT_ALL(line, r'(?:[^,]+,){4}[^,]+|(?:[^,]+,)*[^,]+')) x WITH OFFSET page;

SELECT * FROM temp_result WHERE ARRAY_LENGTH(SPLIT(x)) = 5 UNION ALL
SELECT -1, page, x FROM (
  SELECT STRING_AGG(x) line FROM temp_result WHERE ARRAY_LENGTH(SPLIT(x)) < 5
), UNNEST(REGEXP_EXTRACT_ALL(line, r'(?:[^,]+,){4}[^,]+|(?:[^,]+,)*[^,]+')) x WITH OFFSET page
其中产生以下内容,至少有无-非完整页面-2000万页,每个页面包含5个项目


注意:我确实理解这很可能不是OP所期望的,但这是我所能做到的最好的,它实际上可以在100万行上正常工作

根据前面的讨论,源表也需要排序以保持顺序。问题是,如何对太大的表进行全局排序。以下是我的想法:

由于整个表无法排序,如果成本不是问题,那么大表可以分段并按段排序。 为了均匀地创建段,我们可以创建一个示例表。下面是从大型表格中查询1/1000数据的示例。 创建或替换表格样本表格作为 从rand<0.001的大表格中选择*; 使用采样表,生成一个段边界点数组,假设我们要将大表分成3部分,查询如下 从中选择阵列值、采样点 从“样本表”中选择*作为行号,按值按1的顺序在分区上按行号-1排序 连接最新[1/3,2/3]采样点 在行号=CASTsample\U point*上,从'sample\U table'中选择count1作为INT64
用于迭代段边界数组,运行原始分页查询,但根据前面的讨论,使用WHERE value>lower_bound和value子句,还需要对源表进行排序以保持顺序。问题是,如何对太大的表进行全局排序。以下是我的想法:

由于整个表无法排序,如果成本不是问题,那么大表可以分段并按段排序。 为了均匀地创建段,我们可以创建一个示例表。下面是从大型表格中查询1/1000数据的示例。 创建或替换表格样本表格作为 从rand<0.001的大表格中选择*; 使用采样表,生成一个段边界点数组,假设我们要将大表分成3部分,查询如下 从中选择阵列值、采样点 从“样本表”中选择*作为行号,按值按1的顺序在分区上按行号-1排序 连接最新[1/3,2/3]采样点 在行号=CASTsample\U point*上,从'sample\U table'中选择count1作为INT64
用于迭代段边界数组,运行原始分页查询,但使用WHERE value>lower_bound子句,当您说3000万个条目时使用value-您的意思是3000万行,每个行都有1、2等值吗?是的,每个分区超过3000万行,按哪个分区?我看不到有人提到有问题的分区。我想我理解了您期望的输出-但我仍然不理解什么是输入数据以及它是如何结构的您在查询中看到了分区吗?这就是导致OOM的原因,因为BQ在单个节点AFAICT上执行内存中的分析函数。我们的目标是取代过去的。。。使用其他分区:当你说3000万个条目时,你是说3000万行,每个行都有一个、两个等值吗?是的,每个分区超过3000万行,按哪个分区?我看不到有人提到有问题的分区
. 我想我理解了您期望的输出-但我仍然不理解什么是输入数据以及它是如何结构的您在查询中看到了分区吗?这就是导致OOM的原因,因为BQ在单个节点AFAICT上执行内存中的分析函数。我们的目标是取代过去的。。。用其他东西分区:我也需要保持顺序,或者可以按字段排序。他的方法可以更新以处理这个问题吗?你的问题中没有提到ORDER BY——无论是在文本中还是在查询中!无论如何-很可能是的-对于选项1,但对于选项2,会有一个问题,因为它将所有剩余的东西混合在一起,以进行标记为group=-1的最终拆分。没有,但顺序被隐式保留,我不关心顺序,我可以在原始查询中用RAND替换ROW\u NUMEBR,得到如下结果:SELECT page,ARRAY\u AGGnum as nums FROM SELECT*,CASTRAND*$NUM_PAGES AS INT64 AS PAGES FROM input GROUP BY page我重复我的观点-您的问题中没有任何内容,包括显式或隐式暗示顺序的代码。而且,顺便说一句,ROW_编号本身并不能保证任何订单,所以如果您认为它能保证,请不要上当!同时,我的回答显然没有考虑任何顺序。所以,忽略它,希望有人会给你更好的答案:oI也需要保持秩序,或者可以按字段排序。他的方法可以更新以处理这个问题吗?你的问题中没有提到ORDER BY——无论是在文本中还是在查询中!无论如何-很可能是的-对于选项1,但对于选项2,会有一个问题,因为它将所有剩余的东西混合在一起,以进行标记为group=-1的最终拆分。没有,但顺序被隐式保留,我不关心顺序,我可以在原始查询中用RAND替换ROW\u NUMEBR,得到如下结果:SELECT page,ARRAY\u AGGnum as nums FROM SELECT*,CASTRAND*$NUM_PAGES AS INT64 AS PAGES FROM input GROUP BY page我重复我的观点-您的问题中没有任何内容,包括显式或隐式暗示顺序的代码。而且,顺便说一句,ROW_编号本身并不能保证任何订单,所以如果您认为它能保证,请不要上当!同时,我的回答显然没有考虑任何顺序。所以,忽略它吧,希望有人会给你更好的答案:哦