我正在为来自客户端的自定义数据创建适配器。我不能更改他们的架构或修改他们表中的值,但我可以建议新的索引。方法是使用 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 替换,然后有一个插入 WHERE 子句的 CTE,然后有一个执行所有 NULL 替换的外部 CTE。这样的查询可以使用索引。(我已经验证了这一点。)但是,where 子句不能再期望对默认值的值的测试也会选择具有 NULL 值的记录,因为这种替换还没有发生。
有没有办法执行空替换,允许 SARGABLE where 过滤器,并过滤 NULL 值,就好像它们保持默认值一样?
关于问题大小的注意事项:一个典型的例子涉及将一个 600 万条记录表连接到一个 700 万条记录表,并使用多对多关系创建 1200 万条记录。当过滤器为 SARGABLE 时,查询大约需要 10 秒。当它不是 SARGABLE 时,在一台机器上需要 10 多分钟,在更快的机器上需要 3 分钟以上。
对所选解决方案的评论:
巧妙地使用交集来允许将字段与 NULL 或没有 ISNULL 或其他非 sargable 函数的非 NULL 进行比较,可以在对遗留查询进行最少更改的情况下插入到我们的代码中。
评论 2:缺少案例
有以下六种情况:
- 所选值不为空且不等于默认值且与过滤器值不匹配。应排除。
- 所选值不为空且不等于默认值并且与过滤器值匹配。应包括。
- 选定的值不为空,并且等于默认值并且与过滤器值不匹配。应排除。
- 所选值不为空,并且 DOES 等于默认值,并且与过滤器值匹配。应包括。
- 所选值为 null 且过滤器值不是默认值。应排除。
- 所选值为空,过滤器值为默认值。应包括。
案例 4 无法使用提供的解决方案。所选字段不为空,因此交叉点的前半部分有一条非空值的记录。但是在交集的后半部分,NULLIF 语句创建了一条具有空值的记录。交集产生零记录。记录被拒绝。我仍在寻找处理这种情况的解决方案。很近...
更新解决方案:
我有一个修复。假设我正在使用 [County Name],而我的默认值是“未知”...
where EXISTS (
select [County Name]
intersect
(select NULLIF('User selected county name', 'Unknown') union select 'User selected county name')
)