视图上的过滤器应用于视图中排除的过滤器



我有一个表,它可以在一列中包含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语句。

有什么建议吗?

目前的情况是,不,你不能告诉编译器按照哪个顺序进行筛选,尽管你可以做一些技巧,通常会迫使它这样做。

问题是你的查询被有效地编译成这样:

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%'

最新更新