BigQuery:使用 ROW_NUMBER 函数创建视图会破坏分区过滤策略

BigQuery: Create View using ROW_NUMBER function breaks partition filter policy

我们在 BQ 中创建了一个 table,'TS' 列在创建 table 时用作分区列,如“PARTITION BY DATE(TS)”。我们设置“require_partition_filter=true”

当我们像下面这样创建视图时,对视图的查询有效:

CREATE OR REPLACE VIEW mydataset.test_view AS select * from 
mydataset.test_table
--query based on view
select * from mydataset.test_view where TS > TIMESTAMP("2021-09-05 08:30:00")

但是,如果我们在视图创建语句中添加 ROW_NUMBER() 函数,视图上的相同查询将引发错误:

CREATE OR REPLACE VIEW mydataset.test_view AS 
select *, ROW_NUMBER() over (
partition by ID
order by ID, TS
) as row_number from mydataset.test_table
--query based on view return error
select * from mydataset.test_view where TS > TIMESTAMP("2021-09-05 08:30:00")
--error msg: Cannot query over table without a filter over column(s) 'TS' 
that can be used for partition elimination

这是什么原因,有什么解决办法吗?欣赏任何 ideas/thoughts。谢谢

更新: 我们还尝试在视图创建中添加 'where'。但是,对于我们的案例,我们不能限制通过视图公开的真实数据时间范围,用户希望能够从视图中查询所有数据。这意味着我们只能使用 'TS is not null' 或 'TS > TIMESTAMP("1970-01-01 00:00:00").' 等始终为真的条件来过滤 TS 通过这样做,视图上的查询不会抛出错误,但它的性能非常差,因为在执行时实际上没有分区修剪查询甚至我们在视图上查询时添加额外的 TS 过滤器。

视图实际上只是原始分区 table 的子查询。您的第一个语句有效,因为创建的视图查询 在分区字段 ts 上过滤的。来自视图查询的过滤器被传递到分区 table。 BigQuery 似乎认识到 SELECT * 只是 return 是完整的 table,因此它必须完全绕过该步骤,因此它实际上 return 整个分区 table.

第二个不起作用的原因是因为创建的视图查询

ROW_NUMBER() over (partition by ID order by ID, TS)

在 over 子句中查询整个分区 table(这意味着它是 过滤,分区 table 需要),因为它有尚未评估数据的 WHERE,因为它正在命名的 window.

中创建自己的分区

根据此 doc 当您启用 require_partition_filter=true 时,当您尝试查询 table 时没有WHERE 子句会抛出您遇到的错误。

从您创建的第一个视图和查询中,您已经传递了 where 子句,因此您得到了输出。

在第二个视图中,您添加了 ROW_NUMBER() 函数,并且在查询该视图时收到错误消息。错误是由于您在查询中传递的过滤条件造成的。

我尝试用一​​组示例数据重现您的问题。解决方法是创建一个具有 Where 条件的视图(使用源 table 中的数据子集),然后根据需要查询该视图。您可以参考以下查询,如果此解决方法对您有帮助,请告诉我:

正在创建视图:

CREATE OR REPLACE VIEW dataset2.view3 AS
select *, ROW_NUMBER() over (
partition by id
order by id, ts
) as rownum from `myproject.dataset2.part4` where ts > TIMESTAMP("2021-09-05 08:30:00")

在 ROW_NUMBER() 的视图中,我在查询中添加了 Where 子句。

查询:

Select * from `myproject.dataset2.view3`

输出:

如果您在查询视图时添加另一个 where 子句,它将起作用并且不会抛出任何错误。

查询:

SELECT * FROM `myproject.dataset2.view3` where ts > TIMESTAMP("2021-10-30 10:20:02")

输出:

根据您的回复,由于您无法限制通过视图公开的真实数据时间范围,您可以参考以下案例:

案例一(按id分区):

CREATE OR REPLACE VIEW dataset2.view8 AS
select *, ROW_NUMBER() over (
partition by id
order by id,ts
) as rownum from `myproject.dataset2.part4` where ts IS NOT NULL

查询 1(在视图上):

SELECT * FROM `myprojectproject.dataset2.view8`

它处理了 160B 的数据。

查询 2(在视图上):

SELECT * FROM `myproject.dataset2.view 8` where ts > TIMESTAMP("2021-09-05 08:30:00")

它还处理了 160B 的数据,这意味着如果您按 ID 进行分区,则不会进行分区修剪。

案例2(按ts分区):

CREATE OR REPLACE VIEW dataset2.view8 AS
select *, ROW_NUMBER() over (
partition by ts
order by id,ts
) as rownum from `myproject.dataset2.part4` where ts IS NOT NULL

Query1(在视图上):

SELECT * FROM `myproject.dataset2.view 8`

它处理了 160B 的数据。

Query2(在视图上):

SELECT * FROM `myproject.dataset2.view 8` where ts > TIMESTAMP("2021-09-05 08:30:00")

返回了128B的数据,说明ts分区做了分区剪枝

由于您的要求是实现分区修剪,我尝试了 ts 分区,因为在创建视图后,您将查询视图而不是 table。因此,要实现分区修剪,请在创建视图时尝试使用相同的分区条件,即 partition by ts,您在创建 table 时使用过,您可以使用 WHERE 子句在查询视图时。

如果这不能满足您的要求,请提供您的示例数据和输出,并解释为什么要在视图中按 id 分区的用例。

您可以使用 table 函数代替视图。

Documentation link

CREATE OR REPLACE TABLE FUNCTION mydataset.test_tablefunction (fromTime timestamp)
AS 
select *, ROW_NUMBER() over (
partition by ID
order by ID, TS
) as row_number from mydataset.test_table
where TS > fromTime

用法示例:

select * from mydataset.test_tablefunction(TIMESTAMP("2021-09-05 08:30:00"))