2009-12-22 147 views
2

我有类似下面的查询:在where语句中使用isnull会导致使用索引时出现问题?

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

我已经制定的指标,我认为应该导致有索引查找为= 'N'一部分,而是我看到一个非常昂贵的索引扫描。索引是否可能会搞乱索引的正确使用?在只有几个可能值的列上有索引(如DeleteFlag)?

+0

是否有原因,DeleteFlag的数据类型不是位? – 2009-12-22 22:24:26

+0

要回答第二个问题,在决定是否创建索引时,可能值的数目与数据如何分区并不重要。一般来说,列的选择性越高(即每列值越多可能的行数),索引层次结构中应该越高。 – Aaronaught 2009-12-22 22:28:48

+0

这个项目是新的,所以我不确定,但这是我在未来的潜在变化中留在脑海里的东西。我目前的重点是解决严重的性能问题。如果可能的话,最好有专栏,我认为这不会引起任何严重问题,是吗? – 2009-12-22 22:29:45

回答

8

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

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

指数有道理的,如果你从查询结果预期的数量大于表中的行的总数小得多。

+0

很高兴知道,谢谢。 – 2009-12-22 22:31:04

6

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

2)在选择性非常低(2-3个值)的柱子上有索引是否有意义?是的,但从来没有作为独立的索引表达。索引tipping point将在低选择性色谱柱上创建一个独立索引,这只是浪费空间。但是,当使用更多的键组合时,像位和标志这样的非常低选择性的列在索引中最左边的键是非常有用的。在你的情况下,鉴于这是删除标志,这将是有意义的聚簇索引的第一个关键,因为预计每查询将指定'IsDeleted'条件。

我还会补充说,你应该在'deleted'标志上不应该有NULL。

+2

来吧 - 如果记录是*半*或*部分*删除? =) – 2009-12-22 23:01:35

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语句不返回任何行,因此不等于基线语句,因此不起作用。第三条语句是编写这个查询的另一种方法,其中一个工作,另外一个确保可以使用该字段上的索引。

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

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”,给它是''(一个空字符串)的默认值。然后,查询可以简单地写入而不用担心NULL:

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