我有如下查询:
SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE ISNULL(t1.DeleteFlag,'N') = 'N'
我有一个索引,我认为应该会导致对
= 'N'
部分进行索引查找,但我看到的是非常昂贵的索引扫描。 ISNULL 是否有可能扰乱索引的正确使用? 在只有几个可能值的列上建立索引是否有意义(就像 DeleteFlag
那样)?
是的,WHERE 子句中的任何函数调用都可能使索引变得无用。尝试重写一下,这样索引就可以用了:
SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE NOT t1.DeleteFlag = 'Y'
如果您期望的查询结果数远小于表中的总行数,则索引有意义。
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'
使用 ISNULL 是否会将 Seek 变成 Scan?是的。通常,将函数应用于列会使表达式不可 SARG(不可搜索)。为了使索引被考虑用于查找操作,引擎需要知道要查找什么值,作为原始二进制值。一旦您将函数应用于列,您就会要求搜索该函数的结果,因此它必须在每一行上评估该函数,以查看结果是否满足条件。
在选择性非常低(2-3 个值)的列上建立索引是否有意义?是的,但决不能作为独立的索引表达式。索引“临界点”将使低选择性列上的独立索引浪费空间。但是,当与更多键组合时,像位和标志这样的选择性非常低的列作为索引中最左边的键非常有用。在您的情况下,考虑到已删除标志,作为聚集索引的第一个键是有意义的,因为预计 every 查询将指定“IsDeleted”条件。