按 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 中使用它。
预期输出:
返回每个段(国家、产品、语言)具有最大值的列名,并重命名表结果中的列名。
【问题讨论】:
这些细分是预定义的 - 国家、产品、语言吗?列名的模式是假设您没有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 中列段的最大值返回列名的主要内容,如果未能解决你的问题,请参考以下文章