BigQuery argmax:执行 CROSS JOIN UNNEST 时是否维护数组顺序

BigQuery argmax: Is array order maintained when doing CROSS JOIN UNNEST

问题:

在 BigQuery 中,标准 SQL,如果我 运行

SELECT *
FROM mytable
CROSS JOIN UNNEST(mytable.array)

我可以确定生成的行顺序与数组顺序相同吗?

示例:

假设我有以下 table mytable:

Row | id   | prediction
1   | abcd | [0.2, 0.5, 0.3]

如果我运行 SELECT * FROM mytable CROSS JOIN UNNEST(mytable.prediction),我能确定行顺序和数组顺序一样吗? IE。结果 table 将始终是:

Row | id   | unnested_prediction
1   | abcd | 0.2
2   | abcd | 0.5
3   | abcd | 0.3

有关用例 (argmax) 的更多背景信息:

我试图在每一行中找到数组的最大值 (argmax) 的数组索引,即上面数组中的第二个元素 (0.5)。因此,我的目标输出是这样的:

Row | id   | argmax
1   | abcd | 2

使用 CROSS JOINDENSE_RANK window 函数按 prediction 值和 ROW_NUMBER window 函数查找 argmax ,我可以使用一些测试数据来完成这项工作。您可以使用此查询进行验证:

WITH predictions AS (
  SELECT 'abcd' AS id, [0.2, 0.5, 0.3] AS prediction
  UNION ALL
  SELECT 'efgh' AS id, [0.7, 0.2, 0.1] AS prediction
),
ranked_predictions AS (
  SELECT 
    id,
    ROW_NUMBER() OVER (PARTITION BY id) AS rownum, -- This is the ordering I'm curious about
    DENSE_RANK() OVER (PARTITION BY id ORDER BY flattened_prediction DESC) AS array_rank
  FROM
     predictions P
  CROSS JOIN
    UNNEST(P.prediction) AS flattened_prediction
)
SELECT
  id,
  rownum AS argmax
FROM
  ranked_predictions
WHERE array_rank = 1

ROW_NUMBER 在我的测试中表现良好(即它根据未嵌套数组排序)可能只是巧合,所以最好确定一下。

似乎默认情况下它保持数组的顺序不变。

但是,一种 100% 确定的可能方法是施加某种无关紧要的排序,这将告诉 BQ 黑盒中的查询处理器不要使用任何类型的默认排序,如果它尝试的话。

类似于:

WITH predictions AS (
  SELECT 'abcd' AS id, [2.1, 0.1, 0.1, 0.2] AS prediction
)
select id, p from predictions
cross join unnest(prediction) p
order by 1=1

简答:不,不能保证维持秩序。

长答案:在实践中,您很可能会看到订单得到维护,但您不应该依赖它。您提供的示例类似于此类查询:

SELECT *
FROM (
  SELECT 3 AS x UNION ALL
  SELECT 2 UNION ALL
  SELECT 1
  ORDER BY x
)

输出的预期顺序是什么? ORDER BY 在子查询中,外部查询不强加任何顺序,因此 BigQuery(或任何你 运行 在其中的引擎)可以自由地重新排序输出中的行,因为它认为合适.您最终可能会收到 1, 2, 3,或者您可能会收到 3, 2, 1 或任何其他订单。更一般的原则是投影不保序。

虽然数组的元素具有明确定义的顺序,但当您使用 UNNEST 函数时,您将数组转换为一个关系,它没有明确定义的顺序,除非您使用 ORDER BY。例如,考虑这个查询:

SELECT ARRAY(SELECT x + 1 FROM UNNEST(arr) AS x) AS new_arr
FROM (SELECT [1, 2, 3] AS arr)

new_arr 数组实际上不能保证按该顺序包含元素 [2, 3, 4],因为 ARRAY 函数内的查询不使用 ORDER BY .您可以通过基于元素偏移量的排序来解决这种不确定性,但是:

SELECT ARRAY(SELECT x + 1 FROM UNNEST(arr) AS x WITH OFFSET ORDER BY OFFSET) AS new_arr
FROM (SELECT [1, 2, 3] AS arr)

现在输出保证是[2, 3, 4]

回到你原来的问题,你可以通过在计算行号的子查询中强加一个顺序来确保你得到确定的输出:

ranked_predictions AS (
  SELECT 
    id,
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY OFFSET) AS rownum,
    DENSE_RANK() OVER (PARTITION BY id ORDER BY flattened_prediction DESC) AS array_rank
  FROM
     predictions P
  CROSS JOIN
    UNNEST(P.prediction) AS flattened_prediction WITH OFFSET
)

我在 UNNEST 之后添加了 WITH OFFSET,在 ROW_NUMBER window 中添加了 ORDER BY OFFSET 以确保行号是基于计算的关于数组元素的原始排序。

Can I be certain that the resulting row order is the same as the array order?

您应该使用 WITH OFFSET 来获取数组中元素的位置,然后您可以使用它们在您的进一步逻辑中进行排序

#standardSQL
WITH `project.dataset.table` AS (
  SELECT 'abcd' id, [0.2, 0.5, 0.3] prediction
)
SELECT id, unnested_prediction
FROM `project.dataset.table`, 
UNNEST(prediction) unnested_prediction WITH OFFSET pos
ORDER BY id, pos