6

简化,我有两张桌子,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
);

我想查看哪些联系人与特定 DoNotCall 电话列表中的电话号码匹配。为了更快地查找,我donotcalllist_id和上进行了索引phone

当我进行以下 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)  

执行计划截图

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

执行计划截图

Pastebin 上的执行计划

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

4

2 回答 2

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)

从您发布的查询计划中,它显示第一个计划估计会产生 40k 行,但它只返回 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

您可以使用以下方法手动更新它们:

EXEC sp_updatestats;
于 2013-09-04T12:47:43.997 回答
0

With this poor database structure, a UNION ALL query might be fastest.

于 2013-09-04T17:59:04.470 回答