我们有一个表,其中包含所有准备发送和已发送的电子邮件。该表包含超过 100 万行。
下面是查找仍需要发送的消息的查询。出现 5 次错误后,不再尝试该消息,需要手动修复。 在发送消息之前,SentDate
将保持null
状态。
SELECT TOP (15)
ID,
FromEmailAddress,
FromEmailDisplayName,
ReplyToEmailAddress,
ToEmailAddresses,
CCEmailAddresses,
BCCEmailAddresses,
[Subject],
Body,
AttachmentUrl
FROM sysEmailMessage
WHERE ErrorCount < 5
AND SentDate IS NULL
ORDER BY CreatedDate
查询很慢,我假设由于缺少索引。我已向数据库引擎优化顾问提供了查询。它建议以下索引(以及一些我通常忽略的统计数据):
SET ANSI_PADDING ON
CREATE NONCLUSTERED INDEX [_dta_index_sysEmailMessage_7_1703677117__K14_K1_K12_5_6_7_8_9_10_11_15_17_18] ON [dbo].[sysEmailMessage]
(
[SentDate] ASC,
[ID] ASC,
[ErrorCount] ASC
)
INCLUDE ( [FromEmailAddress],
[ToEmailAddresses],
[CCEmailAddresses],
[BCCEmailAddresses],
[Subject],
[Body],
[AttachmentUrl],
[CreatedDate],
[FromEmailDisplayName],
[ReplyToEmailAddress]) WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
(附带说明:此索引的建议大小为 5,850,573 KB (?),这是 6 GB,对我来说根本没有任何意义。
我的问题是这个建议的索引有意义吗?例如,为什么包含ID
列,而查询中不需要它(据我所知)?就我对索引的了解而言,它们旨在快速查找以查找相关行。如果我必须自己设计索引,我会想出这样的东西:
SET ANSI_PADDING ON
CREATE NONCLUSTERED INDEX [index_alternative_a] ON [dbo].[sysEmailMessage]
(
[SentDate] ASC,
[ErrorCount] ASC
)
WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
优化器是否真的很聪明,或者我的索引效率更高,可能更好?
选择索引有两个不同的方面,查找行所需的字段(=实际索引字段)以及之后所需的字段(=包含字段)。如果您总是在做前 15 行,您可以完全忽略包含的字段,因为 15 次键查找会很快 - 并且将整个电子邮件添加到索引中会使其变得巨大。
对于索引字段,了解与您的条件匹配的数据百分比非常重要。
假设几乎所有行的 ErrorCount <5,您不应该在索引中拥有它 - 但如果这种情况很少见,那么最好有它。
假设 SentDate 很少是 NULL,那么您应该将其作为索引的第一列。
索引中的 CreatedDate 取决于从具有 ErrorCount 和 SentDate 条件的表中找到的平均行数。如果它很多(数千个),那么在那里可能会有所帮助,这样就可以快速找到最新的。
但与往常一样,有几件事会影响性能,因此您应该测试不同的选项如何影响您的环境。