BigQuery argmax:执行CROSS JOIN UNNEST

时间:2018-12-05 14:54:48

标签: arrays google-bigquery cross-join argmax

问题:

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

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

我可以确定结果行顺序与数组顺序相同吗?

示例:

假设我有下表mytable

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

如果我运行SELECT * FROM mytable CROSS JOIN UNNEST(mytable.prediction),可以确定行顺序与数组顺序相同吗?即结果表将始终为:

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 JOIN,一个由DENSE_RANK值排序的prediction窗口函数和一个ROW_NUMBER窗口函数来找到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在我的测试中表现良好(这是根据未嵌套的数组排序的),这可能只是一个巧合,因此可以肯定地说。

3 个答案:

答案 0 :(得分: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函数时,您正在将数组转换为关系,除非使用{ {1}}。例如,考虑以下查询:

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 ) 窗口内的WITH OFFSET之后添加了UNNEST,并在ORDER BY OFFSET窗口内添加了ROW_NUMBER,以确保根据行的原始顺序计算行号。数组元素。

答案 1 :(得分:1)

  

我可以确定结果行顺序与数组顺序相同吗?

您应该使用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  

答案 2 :(得分:0)

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

但是,一种100%可以肯定的方法是强加某种无关紧要的排序,这将告诉BQ黑盒中的查询处理器在尝试时不要使用任何默认排序。

类似的东西:

@Component
public class CustomIpAuthenticationProvider implements AuthenticationProvider {

   private Set<String> whitelist = new HashSet<String>();

    public CustomIpAuthenticationProvider() {
        // 2
        whitelist.add("x.x.x.x");
    }

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      // 3
      WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();
      String userIp = details.getRemoteAddress();
      if(! whitelist.contains(userIp)){
          throw new BadCredentialsException("Invalid IP Address");
      }
      return authentication;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return true; // or false, doesn't make any difference
    }
}