我有以下SQL:
CREATE TABLE tbFoo(
a varchar(50) NULL,
)
CREATE NONCLUSTERED INDEX IX_tbFoo_a ON tbFoo
(
a ASC
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
insert into tbFoo select null
insert into tbFoo select 'test'
以下两个查询正常工作并按预期使用我的索引:
select * from tbFoo where a='test'
select * from tbFoo where a is null
现在,让我假装我想将比较值存储在变量中,如下所示:
declare @a varchar(50)
select @a = NULL
如果@a为null,则以下查询不会返回预期结果,因为我应该使用“is”运算符而不是“=”
select * from tbFoo where a=@a
如果@a为null(由于'test'行强制评估第二个括号),以下方法可以正常进行表扫描
select * from tbFoo where (a is null and @a is null) or (a=@a)
最终,我提出了这个解决方案,它运行正常并使用我的索引:
select * from tbFoo where (a is null and @a is null) or (@a is not null and a=@a)
我对情况的分析是否正确?
有没有更好的方法来处理这种情况?
答案 0 :(得分:3)
最终,我提出了这个解决方案,它运行正常并使用我的索引:
在SQL Server 2008中,您可以根据排除NULL的谓词定义筛选索引:
CREATE UNIQUE NONCLUSTERED INDEX IX_tbFoo_a
ON tbFoo (a)
WHERE a IS NOT NULL;
答案 1 :(得分:1)
没有任何东西“等于”NULL ......这就是NULL的一点。
您的解决方案将正常运行。我对查询优化器如何处理较短版本感到惊讶。我认为在测试与表扫描的相等性之前测试一个for NULL将是一个明智的选择。
答案 2 :(得分:1)
另一种可能性是使用设置ansi nulls来关闭
set ansi_nulls off
declare @a varchar(50)
select @a = NULL
select * from tbFoo where a=@a
set ansi_nulls on
请记住,您正在摆脱此处的默认行为
答案 3 :(得分:1)
也许您的数据库引擎会优化您自动获得的内容,但在我看来,以下内容会更有效:
if @a IS NULL
select * from tbFoo where a is null
else
select * from tbFoo where a = @a
我的理由是,您只需执行一次if @a IS NULL
条件,而不是检查数据库中的每一行。但是,质量数据库引擎应该能够将您的代码转换为与此相同的数据计划。
答案 4 :(得分:0)
这就是我所做的。它非常灵活。 我假设@a是sproc的参数。 'somethingweird'可能是你在记录集中看不到的东西'~~~'或其他什么。
set @a = isnull(@a,'somethingweird')
select * from tbFoo where isnull(a,'somethingweird')=@a
答案 5 :(得分:0)
我家里没有一个实例可以玩,但我可以看到桌面扫描变得非常烦人。一种可能的替代方法是使用UNION代替OR运算符......
select * from tbFoo where (a is null and @a is null)
UNION ALL
select * from tbFoo where (a=@a and @a is not null)
(我不确定“@a is not null”会对性能产生什么影响,但我的直觉是包含它。这是一个常量表达式,应该允许优化器知道整个条件总是失败。我的技术总是发挥,看看什么效果最好。)
我发现这个UNION技巧有两个属性:
- 它可以通过简化查询来显着提高性能
- 它会使代码具有多个连接,并导致严重的维护问题
然而,生活是一种平衡的行为:)
答案 6 :(得分:0)
只是ISNULL双方都这样......
DECLARE @random VARCHAR(50)
SELECT @random = 'text that never appears in your table'
SELECT * FROM @tbFoo WHERE ISNULL(a, @random) = ISNULL(@a, @random)
答案 7 :(得分:0)
你的分析是正确的 - 这就是三值逻辑使生活变得困难的原因。
@StriplingWarrior的建议很好;它通过执行不同的SQL来解决问题,具体取决于变量是否为null。如果不可能,那么你需要重复使用主变量的冗长解决方案。