BigQuery 的 QUALIFY 子句在窗口函数的位置上以不同方式访问列?

问题描述 投票:0回答:1

我正在使用 BigQuery 并遇到了 QUALIFY 子句,我知道它可用于基于窗口函数过滤结果。但是,我注意到 QUALIFY 子句如何访问列方面存在一些细微差别,特别是取决于窗口定义的位置。

有人可以澄清 QUALIFY 子句如何与 FROM 子句和 SELECT 列表别名中的列相关吗?

google-bigquery window-functions
1个回答
0
投票

观察力很棒!在 BigQuery 的 SQL 中,确实有两种类型的列:

  1. 通过 FROM 子句可用的列。
  2. 作为 SELECT 列表别名的列。

QUALIFY 子句的行为根据窗口函数所在的位置而有所不同。让我们使用以下数据集深入研究示例以进行澄清:

WITH a AS (
    SELECT 
        *
    FROM UNNEST([STRUCT(1 AS val, 'in' AS position),
    (2, 'out'),
    (3, 'above'),
    (1, 'in')
    ])
)
场景1:在SELECT列表中定义窗口函数

当您在 SELECT 列表中定义窗口函数时,QUALIFY 只能访问通过 FROM 子句可用的列。

示例 1.1:这是一个有效的查询,因为 QUALIFY 子句基于 rn 列进行过滤,该列源自 FROM 子句中的位置列。

SELECT
    CASE WHEN position = 'above' THEN 'in'
    ELSE position
    END AS mapped_position,
    val,
    ROW_NUMBER() OVER w1 as rn,
FROM a
QUALIFY rn = 1
WINDOW w1 AS (PARTITION BY position)

示例 1.2:下面的查询无效。当 QUALIFY 尝试根据 rn 列进行过滤时,窗口函数使用mapped_position,它是 SELECT 列表中的别名。因此,此时它无法访问mapped_position。

SELECT
    CASE WHEN position = 'above' THEN 'in'
    ELSE position
    END AS mapped_position,
    val,
    ROW_NUMBER() OVER w1 as rn,
FROM a
QUALIFY rn = 1
WINDOW w1 AS (PARTITION BY mapped_position)
场景 2:直接在 QUALIFY 子句中定义窗口函数

当您直接在 QUALIFY 子句中定义窗口函数时,它可以访问 FROM 子句中的列和作为 SELECT 列表别名的列。

示例 2.1:这是一个有效的查询。 QUALIFY 基于窗口函数进行过滤,该窗口函数按 FROM 子句中的

position
列进行分区。

SELECT
    CASE WHEN position = 'above' THEN 'in'
    ELSE position
    END AS mapped_position,
    val
FROM a
QUALIFY ROW_NUMBER() OVER (PARTITION BY position) = 1

例2.2:这个查询是有效的,窗口函数在QUALIFY中定义,通过mapped_position进行分区,这是SELECT列表中的别名。所以,此时它可以访问

mapped_position

SELECT
    CASE WHEN position = 'above' THEN 'in'
    ELSE position
    END AS mapped_position,
    val
FROM a
QUALIFY ROW_NUMBER() OVER (PARTITION BY mapped_position) = 1

总而言之,窗口定义的位置会影响 QUALIFY 子句可以访问的列。在 SELECT 列表内定义窗口函数时,QUALIFY 只能访问 FROM 子句中的列。但是,当直接在 QUALIFY 中定义窗口函数时,它可以访问 FROM 子句中的列以及 SELECT 子句中定义的别名。

知道了,猜猜这个 SQL 会返回什么结果?

WITH a AS (
    SELECT 
        *
    FROM UNNEST(
        [STRUCT(1 AS val, 'in' AS position),
        (2, 'out'),
        (3, 'above'),
        (1, 'in')
        ])
)

SELECT
    CASE WHEN position = 'above' THEN 'in'
    ELSE position
    END AS position,
    val
FROM a
QUALIFY ROW_NUMBER() OVER (PARTITION BY position) = 1
© www.soinside.com 2019 - 2024. All rights reserved.