【问题标题】:In BigQuery, concatenate columns in sorted order based on 2nd table of values在 BigQuery 中,根据第二个值表按排序顺序连接列
【发布时间】:2020-12-05 17:49:49
【问题描述】:

我有以下可重现的示例,因为标题可能不是 100% 清楚,示例应该有所帮助:

with
  
  player_heights as (
    select 'joe' as name, 0.35 as height union all
    select 'tom' as name, 0.75 as height union all
    select 'nick' as name, 0.2 as height union all
    select 'sal' as name, 1.2 as height union all
    select 'chris' as name, 0.5 as height union all
    select 'cob' as name, null as height union all
    select 'jeff' as name, 1.1 as height union all
    select 'pob' as name, 0.71 as height
  ),
  
  players_table as (
    select 'joe' as p1, 'tom' as p2, 'nick' as p3, 'sal' as p4, 'chris' as p5, 0.35 as h1, 0.75 as h2, 0.2 as h3, 1.2 as h4, 0.5 as h5 union all
    select 'joe' as p1, 'nick' as p2, 'cob' as p3, 'jeff' as p4, 'pob' as p5, 0.35 as h1, 0.2 as h2, null as h3, 1.1 as h4, 0.71 as h5 union all
    select 'tom' as p1, 'chris' as p2, 'sal' as p3, 'jeff' as p4, 'pob' as p5, 0.75 as h1, 0.5 as h2, 1.2 as h3, 1.1 as h4, 0.71 as h5
  )
  

select
  concat(p1, '-', p2, '-', p3, '-', p4, '-', p5) as players
  ,* 
from players_table

每个人都与一个身高相关联,来自player_heights 表。在players_table 中,每行有 5 个人,每个人的身高都已连接到桌子上。

对于players_table 中的每一行,需要将 5 个玩家连接成一个字符串。挑战在于这些玩家应该根据他们的身高进行排序,从最小到最大,null height person 在连接字符串的末尾。目前,我正在使用的基本concat 中没有考虑高度。第二行中players 列的正确输出将是nick-joe-pob-jeff-cob

编辑

我考虑过使用嵌套的 case when 语句,但是 5 个人有 120 种可能的玩家排序,这对于 case when 来说似乎太多了

编辑 2

如果这是不可能的,那么另一个可行的解决方案是在concat 之前按字母顺序对人员进行排序。这并不理想,但可能更简单。

更新

添加到最终选择 ARRAY(SELECT x FROM UNNEST(array<string>[p1, p2, p3, p4, p5]) AS x ORDER BY x) AS arr2 的以下列确实从 5 个字符串列创建了一个数组,然后对它们进行排序。所以这是朝着正确的方向发展,但我还没有办法在这里使用额外的高度值。

select
  *
  ,array_to_string(
    array(select x from unnest(array[p1, p2, p3, p4, p5]) as x order by x),
    '-'
  ) as players

【问题讨论】:

    标签: google-bigquery


    【解决方案1】:

    以下是 BigQuery 标准 SQL

    #standardSQL 
    WITH players_table AS (
      SELECT 'joe' AS p1, 'tom' AS p2, 'nick' AS p3, 'sal' AS p4, 'chris' AS p5, 0.35 AS h1, 0.75 AS h2, 0.2 AS h3, 1.2 AS h4, 0.5 AS h5 UNION ALL
      SELECT 'joe' AS p1, 'nick' AS p2, 'cob' AS p3, 'jeff' AS p4, 'pob' AS p5, 0.35 AS h1, 0.2 AS h2, NULL AS h3, 1.1 AS h4, 0.71 AS h5 UNION ALL
      SELECT 'tom' AS p1, 'chris' AS p2, 'sal' AS p3, 'jeff' AS p4, 'pob' AS p5, 0.75 AS h1, 0.5 AS h2, 1.2 AS h3, 1.1 AS h4, 0.71 AS h5
    )
    SELECT STRING_AGG(p, '-' ORDER BY h) AS players, 
      ANY_VALUE(t).*
    FROM players_table t
    LEFT JOIN UNNEST([p1, p2, p3, p4, p5]) p WITH OFFSET
    LEFT JOIN UNNEST([h1, h2, h3, h4, h5]) h WITH OFFSET
    USING(OFFSET)
    GROUP BY FORMAT('%t', t)   
    

    有输出

    【讨论】:

    • 这会显着减慢我在 20GB 表上工作的查询,从 2 分钟到 8 分钟。这种方法是否有一些非常昂贵的计算?
    • 据我了解,您的查询没有做您需要做的事情 - 而我的回答中的查询可以做到! - 正确的?所以很难比较做不同事情的查询 - 同时,我会更多地考虑你的情况,如果能找到更优化的解决方案,我会发布新的解决方案:o)
    • @Canovic - 在新答案中查看优化版本。可能/应该会有更好的性能 - 但总体而言,它仍然与您的逻辑所需的一样昂贵
    【解决方案2】:

    以下是优化版

    #standardSQL 
    WITH players_table AS (
      SELECT 'joe' AS p1, 'tom' AS p2, 'nick' AS p3, 'sal' AS p4, 'chris' AS p5, 0.35 AS h1, 0.75 AS h2, 0.2 AS h3, 1.2 AS h4, 0.5 AS h5 UNION ALL
      SELECT 'joe' AS p1, 'nick' AS p2, 'cob' AS p3, 'jeff' AS p4, 'pob' AS p5, 0.35 AS h1, 0.2 AS h2, NULL AS h3, 1.1 AS h4, 0.71 AS h5 UNION ALL
      SELECT 'tom' AS p1, 'chris' AS p2, 'sal' AS p3, 'jeff' AS p4, 'pob' AS p5, 0.75 AS h1, 0.5 AS h2, 1.2 AS h3, 1.1 AS h4, 0.71 AS h5
    )
    SELECT (
        SELECT STRING_AGG(p, '-' ORDER BY h)
        FROM UNNEST([p1, p2, p3, p4, p5]) p WITH OFFSET
        LEFT JOIN UNNEST([h1, h2, h3, h4, h5]) h WITH OFFSET
        USING(OFFSET)
      ) AS players, *
    FROM players_table
    

    【讨论】:

    • 我从未评论过这个优化版本,但由于某种原因,它在我的查询中明显更快。与其将我的查询时间增加 4 倍,它只会增加约 40% 的查询时间,这仍然是一个有意义的减速(60 --> 90 秒),但在实现预期结果时要好得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-22
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多