我有需要加入的视图,这些视图具有过滤器来排除包含无效数据的记录。在视图中,过滤是在剩余记录转换为其各自的数据类型之前完成的,但在加入视图时则不是。即使独立视图不传送无效记录,连接也会失败。
这是一个非常简单的例子:
create or replace table SRC (DTM varchar);
insert into SRC values ('2020-01-01'),('00-00-00');
create or replace view SRC_V as
(
select DTM::timestamp as DTM
from SRC where DTM!='00-00-00'
);
select * from
SRC_V as a
inner join SRC_V as b
ON a.DTM = b.DTM;
这给出了
Timestamp '00-00-00' is not recognized
,因此即使视图过滤掉了该记录,它仍然存在于连接中。
不幸的是,我无法操作视图,并且连接代码是由代码生成器生成的,因此没有很多选项可以为这种情况编写特殊代码,所以我需要先了解这是一个错误还是设计使然我们会做出任何重大改变来适应这一点。
我已经在 SQL Server 中尝试过这种情况,它按我的预期工作。
此外,还有一些可行的变体,例如:
with x as (select DTM from SRC_V)
select * from
x as a
inner join x as b
ON a.DTM = b.DTM;
如果这种行为是设计使然,我觉得这个例子也应该失败。它也适用于
full outer
,但不适用于 left
或 right
外部,这也感觉很奇怪。
这是设计使然。因为您正在执行自联接(联接同一个表),所以优化器知道它可以在两次扫描上应用 where 子句谓词。请注意,该计划有两次
!='00-00-00'
过滤器,一次在 Filter2 中,一次在 Filter4 中:
在扫描上应用 where 子句谓词可以减少(修剪)Snowflake 必须扫描的微分区数量。通过 where 子句进行的修剪发生在执行开始之前,并且通常比计划中的下一个运算符(即 JoinFilter [5])更有效。
至于为什么 CTE 不会发生这种情况,这是一个完全不同的计划。当连接发生时,行已经被扫描并通过过滤器:
但是,使用 CTE 并不能保证它始终以这种方式工作。 Snowflake 优化器可以并且将会根据仓库大小和数据量更改计划。虽然此计划在小规模下有效,但以更高的行数运行它可能会形成一个遇到与视图相同的过滤问题的计划。
您提到有些东西正在生成代码,并且您可能无法访问它,但处理这种情况的最安全方法是更改视图将 varchar 转换为时间戳的方式。使用
::
符号进行转换不会处理错误。您可以让某人可以更改代码和/或视图以使用可以处理错误的东西,如下所示:
create or replace view SRC_V as
(
select try_to_timestamp(DTM) as DTM
from SRC where DTM!='00-00-00'
);
这会将 00-00-00 值转换为数据库
null
,并且由于 null 不等于包括数据库 null 在内的任何内容,因此不会连接这些行。