需要SARGABLE方法来过滤记录,并指定NULL的默认值

Pau*_*och 6 sql sql-server performance

我正在从客户端创建自定义数据的适配器.虽然我可以建议新的索引,但我无法更改其架构或修改其表中的值.方法是使用CTE连接和重新格式化自定义数据以使用我们的列名,枚举值等.重新格式化数据后,可以附加我们的标准CTE,并从中执行可以执行我们的标准分析的查询.

由于LEFT JOIN没有匹配,或者由于其数据中的值实际为NULL,因此重新格式化产生的某些值为NULL.

我的任务是在许多字段中替换NULL的默认值,并允许将WHERE子句插入到查询中.目前,ISNULL调用或CASE语句用于处理默认值.目前,在WHERE条件被命中时,已经执行了此替换,因此有权访问我们的查询构建器的最终用户可以过滤可能是默认值的值.如果过滤器值是默认值,则应选择具有使用默认值替换的NULL值的记录.

问题是如果我有myField = ISNULL(myField,'MyDefault')作为我的重新格式化公式,后来在洋葱的外层(后来的CTE)中有WHERE myField ='MyDefault',那么这个where子句不是sargable:查询优化器不会在myField上选择我的索引.

我发生的部分解决方案是不在我的内部CTE中进行任何NULL替换,然后有一个CTE来获取插入的WHERE子句,然后有一个外部CTE执行所有的NULL替换.这样的查询可以使用索引.(我已经验证了这一点.)然而,where子句不再期望对默认值的值的测试也会拾取具有NULL值的记录,因为尚未发生替换.

有没有办法执行空替换,允许SARGABLE在哪里过滤,并过滤NULL值,就好像它们保持默认值?

关于问题规模的说明:一个典型的例子涉及将一个600万的记录表加入一个700万条记录表,其中多对多关系创建了1200万条记录.当过滤器是SARGABLE时,查询大约需要10秒钟.当它不是SARGABLE时,一台机器需要10分钟以上,机器运行时需要3分钟以上.

关于CHOSEN解决方案的评论:

巧妙地使用交集来允许将字段比较为NULL或非NULL而不使用ISNULL或其他非可搜索函数可以通过对我们的遗留查询的最少更改来检测到我们的代码.

评论2:遗失案件

有这六种情况:

  1. 选定的值不为null,不等于默认值,与过滤器值不匹配.应该排除.
  2. 选定的值不为null,不等于默认值,DOES与过滤器值匹配.应包括.
  3. 选定的值不为空且DOES等于默认值且与过滤器值不匹配.应该排除.
  4. 选定的值不为空,DOES等于默认值,DOES与过滤器值匹配.应包括.
  5. 选定的值为null,筛选器值不是默认值.应该排除.
  6. 选定的值为null,筛选器值为默认值.应包括.

案例4使用提供的解决方案不起作用.所选字段不为空,因此交集的前半部分具有anon-null值的记录.但是在交集的后半部分,NULLIF语句创建了一个空值的记录.交叉点产生零记录.该记录被拒绝.我仍在寻找处理这种情况的解决方案.很近...

更新方案:

我有一个修复.说我适应[县名],我的默认值是'未知'...

where EXISTS (
    select [County Name] 
    intersect 
    (select NULLIF('User selected county name', 'Unknown') union select 'User selected county name')
)
Run Code Online (Sandbox Code Playgroud)

Mik*_*son 3

看起来您已经在动态构建查询,因此当您从工具中获取需要过滤的值时,您可以使用看起来像这样的 where 子句构建查询。

SQL小提琴

MS SQL Server 2008 架构设置

create table YourTable
(
  ID int identity primary key,
  Name varchar(20)
)

create index IX_YourTable_Name on YourTable(Name)

insert into YourTable values
('Name1'),
('Name2'),
(null)
Run Code Online (Sandbox Code Playgroud)

查询1

declare @Param varchar(20)
set @Param = 'DefaultName'

select ID,
       coalesce(Name, 'DefaultName') as Name
from YourTable
where exists(select Name intersect select nullif(@Param, 'DefaultName'))
Run Code Online (Sandbox Code Playgroud)

结果

| ID |        NAME |
--------------------
|  3 | DefaultName |
Run Code Online (Sandbox Code Playgroud)

查询2

declare @Param varchar(20)
set @Param = 'Name1'

select ID,
       coalesce(Name, 'DefaultName') as Name
from YourTable
where exists(select Name intersect select nullif(@Param, 'DefaultName'))
Run Code Online (Sandbox Code Playgroud)

结果

| ID |  NAME |
--------------
|  1 | Name1 |
Run Code Online (Sandbox Code Playgroud)

上述查询的查询计划将使用 IX_YourTable_Name 进行查找。

在此输入图像描述

参考:未记录的查询计划:相等比较