按 BigQuery 中列段的最大值返回列名

Posted

技术标签:

【中文标题】按 BigQuery 中列段的最大值返回列名【英文标题】:Return column name by maximum values for column segment in BigQuery 【发布时间】:2020-06-23 23:03:21 【问题描述】:

我的查询应该是根据列的最大值返回 COLUMN NAME。我尝试使用 CASE WHEN 来解决这个案例。但不知何故会发生此错误,可能是因为我返回列的名称而不是列本身的值:

No matching signature for operator CASE; all THEN/ELSE arguments must be coercible to a common type but found: INT64, STRING; actual argument types (WHEN THEN) ELSE: (BOOL STRING) (BOOL INT64) INT64 at [3:5]

我的代码是:

SELECT
ID,
    CASE
        WHEN col1 >= col2 AND col1 >= col3 AND col1 >= col4 AND col1 >= col5 THEN 'col1 '
        WHEN col2 >= col1 AND col2 >= col3 AND col2 >= col4 AND col2 >= col5 THEN 'col2 '
        ELSE 'col1'                                
    END AS Max_Column_Name
FROM table

示例输入是:

有什么方法可以让这个查询更简单吗?因此,如果有很多列,则无需重复 when..case。其他举措是在post 中使用 GREATEST(col1,col2,col3) 但是,我不确定如何在标准 sql、bigquery 中使用它。

预期输出:

返回每个段(国家、产品、语言)具有最大值的列名,并重命名表结果中的列名。

【问题讨论】:

这些细分是预定义的 - 国家、产品、语言吗?列名的模式是 _XXX ?请澄清一下 6 列也是固定数字还是可以是任何数字 名称的模式与提到的示例输入完全相同。不需要 6 列,可以是任何列,例如在我的情况下会有超过 12 列(例如 Country_XX 有 4 列,Product_XX 有另外 4 列等) 【参考方案1】:

假设您没有NULL 值,您可以使用greatest()

select (case greatest(country_uk, country_us)
            when country_uk then 'uk' when country_us then 'us'
        end), 
       . . .

你也可以使用数组:

select (select el.what
        from unnest(array['uk' as what, country_uk as val), ('us', country_us)]) el
        order by el.val desc
        limit 1
       ) as country

【讨论】:

@swm 。 . .什么“段”?我不知道你指的是什么。 如果存在多个具有相同最大值的列,则此查询是否支持。 (例如,最高的列名是 UK 和 US) @swm 。 . .它的作用与您问题中的示例代码相同。您的问题中没有其他解释,因此这似乎是正确的方法。【参考方案2】:

以下是 BigQuery 标准 SQL 它在某种程度上是非常通用的——如果你有更多或不同的段及其名称——你只需要在大多数外部 SELECT 中反映它——在像 (MAX(IF(segment = 'SegmentName', winner, NULL)) AS SegmentName) 这样的行中,其余的在内部查询中处理

#standardSQL
SELECT id,
  MAX(IF(segment = 'Country', winner, NULL)) AS Country,
  MAX(IF(segment = 'Product', winner, NULL)) AS Product,
  MAX(IF(segment = 'Lang', winner, NULL)) AS Lang
FROM (
  SELECT id, segment, ARRAY_AGG(item ORDER BY col_value DESC LIMIT 1)[OFFSET(0)] winner
  FROM `project.dataset.table` t, 
  UNNEST(ARRAY(
      SELECT AS STRUCT segment, item, SAFE_CAST(col_value AS INT64) AS col_value
      FROM UNNEST(REGEXP_EXTRACT_ALL(TO_JSON_STRING(t), r'"\w+":[^,]+')) kv,
      UNNEST([STRUCT(SPLIT(kv,':')[OFFSET(0)] AS col_name, SPLIT(kv,':')[OFFSET(1)] AS col_value)]) nv,
      UNNEST([STRUCT(SPLIT(TRIM(col_name, '"'), '_')[OFFSET(0)] AS segment, SPLIT(TRIM(col_name, '"'), '_')[OFFSET(1)] AS item)])
      WHERE TRIM(col_name, '"') != 'id'
    )) kv
  GROUP BY id, segment
)
GROUP BY id    

如果适用于您的问题中的示例数据 - 输出是

Row id  Country Product Lang     
1   abc US      A       EN   
2   def UK      B       PH      

注意:最近引入的EXECUTE IMMEDIATE 将允许您进一步概括上述解决方案,因此您甚至无需担心大多数外部 SELECT 中的行 - 我将这个留给您,因为它肯定超出了主要问题范围

【讨论】:

外部 SELECT 中的“段”是预定义的还是来自表? 我使用了我在您的示例中看到的任何内容。如果在您的实际情况下您有不同的 - 只需使用它们 - 将 MAX(IF(segment = 'SegmentName', winner, NULL)) AS SegmentName 替换为您的真实段名称。你最终会在你的外部选择中得到尽可能多的这样的行,就像你拥有的不同的段一样。因此,在您的问题示例中-您有三个部分-因此您有三行-希望它有意义。对我来说听起来很简单,完全按照你的要求做:o)【参考方案3】:
SELECT TOP 1 T.Col
FROM 
(
   SELECT Country_UK AS colMax,'Country_UK' Col FROM table 
    UNION ALL              
   SELECT Country_US AS colMax,'Country_US' Col FROM table 
    UNION ALL              
   SELECT Product_A AS colMax,'Product_A' Col FROM table 
    UNION ALL             
   SELECT Product_B AS colMax,'Product_B' Col FROM table 
    UNION ALL             
   SELECT Lang_EN AS colMax,'Lang_EN' Col FROM table 
) AS T 
GROUP BY T.Col 
Order by MAX(T.colMax) DESC

【讨论】:

以上是关于按 BigQuery 中列段的最大值返回列名的主要内容,如果未能解决你的问题,请参考以下文章

BigQuery 在选择不同行时按一个字段中的最大值分组

代码返回列名,但Kotlin中列数为零

在 BigQuery 中获取按天分组的最大数量的唯一 ID

如何修改此 t-sql 查询以返回不同列名的最大值?

如何为用户返回每月最大日期

报告pyspark数据框中列的前3个最大值[重复]