可以在where语句中使用isnull导致使用索引出现问题吗?

时间:2009-12-22 22:21:21

标签: sql sql-server isnull

我有如下查询:

SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE ISNULL(t1.DeleteFlag,'N') = 'N'

我有一个索引,我认为应该导致= 'N'部分的索引搜索,但我看到一个非常昂贵的索引扫描。索引是否可能搞乱索引的正确使用?在列上只有几个可能的值(如DeleteFlag会)的索引是否有意义?

3 个答案:

答案 0 :(得分:8)

是的,WHERE子句中的任何函数调用都可能使索引无效。尝试重写它以便可以使用索引:

SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE NOT t1.DeleteFlag = 'Y'

如果您希望从查询中获得的结果数远远小于表中的总行数,则索引是有意义的。

答案 1 :(得分:6)

1)ISNULL的使用是否会变成扫描?是。通常将函数应用于列使得表达式不具有SARG能力(不可搜索)。为了使Seek操作考虑索引,引擎需要知道要寻找的值,作为原始二进制值。一旦将函数应用于列,您要求搜索函数的结果,因此它必须评估每一行的函数,以查看结果是否满足条件。

2)在具有极低选择性(2-3个值)的列上有索引是否有意义?是的,但从不作为独立的索引表达式。索引tipping point将在低选择性列上创建独立索引,这只是浪费空间。但是,当使用更多键组合时,非常低的选择性列(如位和标志)作为索引中最左侧的键非常有用。在你的情况下,如果是删除标志,那么成为聚簇索引的第一个键是有意义的,因为预期每个查询将指定'IsDeleted'条件。

我还要补充一点,你应该在'已删除'标志上没有NULL。

答案 2 :(得分:5)

Mark Byers的回答不起作用,这是由于SQL Server处理空值的一种非常微妙的方式。在WHERE表达式“t1.DeleteFlag ='Y'”中,如果t1.DeleteFlag为NULL,则表达式返回NULL。所以做NOT(NULL)也会返回NULL,这会导致WHERE条件失败。尝试做这个测试:

DECLARE @myvar VARCHAR(1)
SET @myvar = NULL

SELECT 'OK' WHERE ISNULL(@myvar, 'N') = 'N' -- Baseline statement. Returns OK
SELECT 'OK' WHERE NOT (@myvar = 'Y')        -- Equivalent to answer above. Fails
SELECT 'OK' WHERE @myvar = 'N' OR @myvar IS NULL -- This is another way to do it. Also returns OK

第二个select语句不返回任何行,因此不等同于baseline语句,因此不起作用。第三个语句是编写此查询的另一种方法,一个,一个,两个,仍然确保可以使用该字段的索引。

所以,这是对这个问题的正确答案:

SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE (t1.DeleteFlag = 'N' OR t1.DeleteFlag IS NULL)

另一种可能会产生名义上更好的性能结果的方法是将DeleteFlag字段定义为“NOT NULL”并给它一个DEFAULT为''(空字符串)。然后,可以简单地编写查询而不用担心NULL:

SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE t1.DeleteFlag = 'N'