视图上的过滤器应用于视图中排除的过滤器
Posted
技术标签:
【中文标题】视图上的过滤器应用于视图中排除的过滤器【英文标题】:Filter on a view is applied to filters excluded in a view 【发布时间】:2021-05-13 18:47:56 【问题描述】:我有一个可以在列中包含 JSON 数据的表。我使用 ISJSON()
函数向表中添加了一个计算列,以标记任何不包含有效 JSON 数据的行
CREATE TABLE tbl1 (Id INT IDENTITY(1,1) NOT NULL, Content NVARCHAR(MAX), IsJsonRecord AS ISJSON(Content))
GO
INSERT INTO tbl1 (Content) VALUES ('a'), ('"name":"asd"')
GO
现在我有一个视图,可以将 JSON 数据解析为更易读的格式,例如
CREATE VIEW vw1
AS
SELECT Id,
JSON_VALUE(Content, '$."name"') AS Name
FROM tbl1
WHERE IsJsonRecord > 0
当我从视图中选择时,WHERE 子句按预期工作。
SELECT *
FROM vw1
当我使用额外的 where 子句查询视图时,由于 JSON 数据格式不正确(如下所示)而出现错误。
SELECT *
FROM vw1
WHERE [Name] LIKE '%a%'
查询 WHERE 子句似乎应用于不符合视图中已指定的 WHERE 子句的行。
这是预期的行为吗?
我知道该视图已“优化掉”,但我希望查询优化器先将过滤器应用到不同的字段,然后再应用需要对数据进行操作的函数的过滤器。我认为逻辑在某些情况下可能会带来性能优势。
我不太确定如何在视图中容纳 WHERE 子句。我的实际情况比示例复杂得多,我不确定是否可以使用CASE
语句而不是JSON_VALUE
语句来测试视图中的每一列。
有什么建议吗?
【问题讨论】:
看起来很奇怪。如果您将json_value(...)
替换为 cross apply openjson(...) with (...)
构造,错误就会消失,但我一开始就没想到会出现错误。
【参考方案1】:
就目前而言,不,您不能告诉编译器按什么顺序进行过滤,尽管您可以采取一些技巧,通常会迫使它这样做。
问题是您的查询是这样有效编译的:
SELECT *
FROM (
SELECT Id,
JSON_VALUE(Content, '$."name"') AS Name
FROM tbl1
WHERE IsJsonRecord > 0
) vw1
WHERE [Name] LIKE '%a%'
然后将其优化为:
SELECT Id,
JSON_VALUE(Content, '$."name"') AS Name
FROM tbl1
WHERE IsJsonRecord > 0 AND [Name] LIKE '%a%'
此时,编译器将决定首先评估哪个部分。不管什么原因,它都选择先做LIKE
,这可能是因为索引很好。
您有多种解决方案:
使用CASE
,保证(某些情况除外)按顺序评估
SELECT Id,
JSON_VALUE(Content, '$."name"') AS Name
FROM tbl1
WHERE CASE WHEN IsJsonRecord > 0 THEN CASE WHEN [Name] LIKE '%a%' THEN 1 END END = 1
在持久列上添加索引,以该列作为前导键,或过滤索引IsJsonRecord > 0
。
这不能保证总是有效,但通常可以。确保包括所有必要的列。
上面的一个变体是在视图中添加一个聚集索引。这样做有很多限制,但它可以很好地工作。确保在查询中添加WITH (NOEXPAND)
提示。
更有保证的选择是在视图中添加TOP
。
这迫使编译器确保IsJsonRecord > 0
在逻辑上首先被评估,这几乎总是意味着它将首先在物理上执行。
SELECT *
FROM (
SELECT TOP (9223372036854775807) *
FROM vw1
) vw1
WHERE [Name] LIKE '%a%'
【讨论】:
感谢您的出色回应,我将不得不进行实验。我发现的另一种方法如下。问题是我的观点被用于其他事情,所以我需要确保这一切都不会破坏这些情况。 ALTER VIEW [dbo].[vw1] AS SELECT Id, CASE WHEN IsJsonRecord = 0 THEN NULL ELSE JSON_VALUE(Content, '$."name"') END AS Name FROM tbl1 WHERE IsJsonRecord > 0 GO以上是关于视图上的过滤器应用于视图中排除的过滤器的主要内容,如果未能解决你的问题,请参考以下文章
bigQuery 和 GA-Premium 集成:从 GA 中的未过滤视图导出数据时,如何在 bigQuery 中使用 IP 过滤器(以排除内部流量)
如何使 pixi js 掩码过滤器响应移动视图上的触摸,就像它在桌面视图中移动指针一样?