JOIN对多列时使用索引

ANi*_*sus 5 sql-server indexing join sql-server-2008

简化,我有两个表,contactsdonotcall

CREATE TABLE contacts
(
    id int PRIMARY KEY,
    phone1 varchar(20) NULL,
    phone2 varchar(20) NULL,
    phone3 varchar(20) NULL,
    phone4 varchar(20) NULL
);
CREATE TABLE donotcall
(
    list_id int NOT NULL,
    phone varchar(20) NOT NULL
);
CREATE NONCLUSTERED INDEX IX_donotcall_list_phone ON donotcall
(
    list_id ASC,
    phone ASC
);
Run Code Online (Sandbox Code Playgroud)

我想看看哪些联系人与DoNotCall手机的特定列表中的电话号码相匹配.为了更快地查找,我已经编入索引donotcalllist_idphone.

当我进行以下JOIN时需要很长时间(例如9秒):

SELECT DISTINCT c.id
FROM contacts c
JOIN donotcall d
    ON d.list_id = 1
    AND d.phone IN (c.phone1, c.phone2, c.phone3, c.phone4)  
Run Code Online (Sandbox Code Playgroud)

执行计划的屏幕截图

Pastebin的执行计划

如果我在每个手机领域单独LEFT JOIN,它运行得更快(例如1.5秒):

SELECT c.id
FROM contacts c
LEFT JOIN donotcall d1
    ON d1.list_id = 1
    AND d1.phone = c.phone1
LEFT JOIN donotcall d2
    ON d2.list_id = 1
    AND d2.phone = c.phone2
LEFT JOIN donotcall d3
    ON d3.list_id = 1
    AND d3.phone = c.phone3
LEFT JOIN donotcall d4
    ON d4.list_id = 1
    AND d4.phone = c.phone4
WHERE
    d1.phone IS NOT NULL
    OR d2.phone IS NOT NULL
    OR d3.phone IS NOT NULL
    OR d4.phone IS NOT NULL
Run Code Online (Sandbox Code Playgroud)

执行计划的屏幕截图

Pastebin的执行计划

我的假设是第一个代码片段运行缓慢,因为它没有使用索引donotcall.
那么,如何对多列进行连接并仍然使用索引?

And*_*mar 6

SQL Server可能认为IN (c.phone1, c.phone2, c.phone3, c.phone4)使用索引进行解析太昂贵了.

您可以使用提示测试索引是否更快:

SELECT c.*
FROM contacts c
JOIN donotcall d with (index(IX_donotcall_list_phone))
    ON d.list_id = 1
    AND d.phone IN (c.phone1, c.phone2, c.phone3, c.phone4)
Run Code Online (Sandbox Code Playgroud)

根据您发布的查询计划,它显示第一个计划估计会产生4万行,但它只返回21行.第二个计划估计1排(当然也会返回21排).

您的统计信息是最新的吗?过时的统计数据可以解释查询分析器做出错误选择的原因.统计数据应自动更新或每周更新一次.使用以下方法检查统计信息的年龄:

select  object_name(ind.object_id) as TableName
,       ind.name as IndexName
,       stats_date(ind.object_id, ind.index_id) as StatisticsDate
from    sys.indexes ind
order by 
        stats_date(ind.object_id, ind.index_id) desc
Run Code Online (Sandbox Code Playgroud)

您可以手动更新它们:

EXEC sp_updatestats;
Run Code Online (Sandbox Code Playgroud)

  • 您是否尝试过比较查询计划?(菜单查询,然后包括实际执行计划.) (2认同)