SQL Server为什么索引不与OR一起使用

时间:2016-11-29 19:50:54

标签: sql sql-server indexing

我一直在研究索引并试图了解它们的工作方式以及如何使用它们来提升性能,但我错过了一些东西。

我有下表:

| Id | Name | Email | Phone |
| 1  | John |  E1   |  P1   |
| 2  | Max  |  E2   |  P2   |

考虑到查询将(大部分时间)属于

形式,我试图找到对列EmailPhone编制索引的最佳方法
[1] SELECT * FROM Person WHERE Email = '...' OR Phone = '...'
[2] SELECT * FROM Person WHERE Email = ...
[3] SELECT * FROM Person WHERE Phone = ...

我认为最好的方法是使用两列创建单个索引:

CREATE NONCLUSTERED INDEX [IX_EmailPhone]
ON [dbo].[Person]([Email], [PhoneNumber]);

但是,使用上面的索引,只有查询[2]受益于索引搜索,其他人使用索引扫描。

我还尝试创建多个索引:一个包含两列,一个用于电子邮件,另一个用于电子邮件。在这种情况下,[2]和[3]使用seek,但[1]继续使用scan。

为什么数据库不能使用或使用索引?考虑到查询,该表的最佳索引方法是什么?

2 个答案:

答案 0 :(得分:1)

为每列创建单独的索引 通过使用提示,我们可以强制优化器使用/不使用索引,因此您可以检查执行计划,了解所涉及的性能并了解每个路径的含义。

浏览我的演示并考虑以下场景中每条路径所涉及的工作 -

  1. 只有少数行满足j = 123的条件 只有少数行满足条件k = 456。

  2. 大多数行满足j = 123的条件 大多数行满足条件k = 456。

  3. 只有少数行满足j = 123的条件 大多数行满足条件k = 456。

  4. 尝试思考您为每种情况选择的路径 请随时提问。

    演示

    ;with t(n) as (select 0 union all select n+1 from t where n < 999)
    
    select      1+t0.n+1000*t1.n                                as i
               ,floor(rand(cast (newid() as varbinary))*1000)   as j
               ,floor(rand(cast (newid() as varbinary))*1000)   as k 
    
    into        t
    
    from        t t0,t t1 
    
    option       (maxrecursion 0)
    ;
    
    create index t_j on t (j);
    create index t_k on t (k);
    
    update statistics t (t_j)
    update statistics t (t_k)
    

    扫描

    select      *
    from        t (forcescan)
    where       j = 123
            or  k = 456
    
    • 这很简单。

    enter image description here

    select      *
    from        t (forceseek)
    where       j = 123
            or  k = 456
    
    • &#34; Index Seek&#34; :正在寻找每个索引的相关值(123和456)
    • &#34;合并加入&#34; :正在连接结果(行ID)(如在UNION ALL中)
    • &#34; Stream Aggregate&#34; :正在删除重复的行ID
    • &#34; Rid Lookup&#34; + &#34;嵌套循环&#34; :行ID用于从表中检索行(t)的

    enter image description here

答案 1 :(得分:0)

使用两个单独的索引,一个在(email)上,另一个在(phone, email)上。

OR相当困难。如果您的条件由AND而不是OR连接,那么您的索引将用于第一个查询(但不是第三个查询,因为phone不是索引中的第一个键)

您可以将查询编写为:

SELECT *
FROM Person 
WHERE Email = '...' 
UNION ALL
SELECT *
FROM Person 
WHERE Email <> '...' AND Phone = '...';

SQL Server应为每个子查询使用适当的索引。