我应该如何针对可以为 null 的 SQL 变量测试字段?
我有以下 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 为空,因为我应该使用“is”运算符而不是“=”
select * from tbFoo where a=@a
以下内容将起作用,但如果 @a 为空,则会进行表扫描(因为“测试”行强制评估第二个括号)
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)
我对情况的分析是否正确?
有没有更好的方法来处理这种情况?
I've got the following 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'
The two following queries work fine and use my index as expected :
select * from tbFoo where a='test'
select * from tbFoo where a is null
Now, let's pretend I want to store my comparison value in a variable, like this :
declare @a varchar(50)
select @a = NULL
The following query won't return the expected results if @a is null because I should use the "is" operator rather than "="
select * from tbFoo where a=@a
The following will work but will do a table scan if @a is null (because of the 'test' row which forces the evaluation of the second parenthesis)
select * from tbFoo where (a is null and @a is null) or (a=@a)
Eventually, I've came up with this solution, which works fine and uses my index :
select * from tbFoo where (a is null and @a is null) or (@a is not null and a=@a)
Is my analysis of the situation correct?
Is there a better way to handle this situation ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
在 SQL Server 2008 中,您可以基于排除 NULL 的谓词定义过滤索引:
In SQL Server 2008, you can define a filtered index based on a predicate that excludes NULLs:
没有什么东西“等于”NULL……这就是 NULL 的要点。
您的解决方案会正常工作。 我对查询优化器如何处理较短版本感到惊讶。 我认为在使用表扫描测试相等性之前测试 a 是否为 NULL 是理所当然的。
Nothing is ever "equal to" NULL... which is sort of the point of NULL.
Your solution will work fine. I'm surprised at how the query optimizer handles the shorter version. I would think that testing a for NULL before testing equality with a table scan would be a no-brainer.
另一种可能性是将 ansi nulls 设置为 off
请记住,您正在脱离此处的默认行为
another possibility is using setting ansi nulls to off
Just keep in mind that you are breaking away from the default behavior here
也许您的数据库引擎会自动优化您所获得的内容,但在我看来,以下内容会更有效:
我对此的推理是您只需执行一次
if @a IS NULL
条件,而不是检查数据库中的每一行。 不过,高质量的数据库引擎应该能够将您的代码转换为与此相同类型的数据计划。Perhaps your database engine optimizes what you've got automatically, but it seems to me like the following would be more efficient:
My reasoning for this is that you would perform the
if @a IS NULL
condition only once, rather than checking it for every line in the database. Again, though, a quality database engine should be able to convert your code into the same kind of data plan as this.这就是我所做的。 它非常灵活。
我假设 @a 是存储过程的参数。 'somethingweird' 可能是您在记录集中永远不会看到的东西 '~~~' 或其他什么。
This what I do. It's very flexible.
I am assuming that @a is an argument for the sproc. 'somethingweird' can be something that you will never see in your recordset '~~~' or whatever.
我家里没有实例可以玩,但我可以看到表扫描变得非常烦人。 一种可能的替代方案是使用 UNION 代替 OR 运算符...
(我不确定“@a 不为空”会对性能产生什么影响,但我的直觉是包含它。这是一个常量表达式应该让优化器知道整个条件何时总是失败。我的技术总是玩并看看什么最有效。)
我发现这个 UNION 技巧有两个属性:
- 它可以通过简化查询来显着提高性能
- 它会通过多个连接使代码膨胀并导致严重的维护麻烦
但是,生活只是一种平衡行为:)
I don't have an instance at home to play with, but I can see the table scan getting very annoying. A possible alternative is to use UNION in place of the OR operator...
(I'm not sure exactly what effect the "@a is not null" will have on performance, but my gut feel would be to include it. It's a constant expression that should allow the optimiser to know when the whole condition always fails. My technique is always to play and see what works best.)
I find this UNION trick to have two properties:
- It can significantly improve performance by simplfying queries
- It balloons code with multiple joins and cause major maintenance headaches
But then, life is jus a balancing act :)
只是 ISNULL 双方都像这样......
Just ISNULL both sides like so...
你的分析是正确的——这就是为什么三值逻辑让生活变得困难的原因。
@StriplingWarrior 的建议很好; 它通过根据变量是否为空执行不同的 SQL 来解决这个问题。 如果这是不可能的,则需要重复使用主机变量的冗长解决方案。
Your analysis is correct - and is why 3-valued logic makes life difficult.
The suggestion from @StriplingWarrior is good; it finesses the problem by executing different SQL depending on whether the variable is null or not. Where that is not possible, your long-winded solution which repeatedly uses the host variable is necessary.