我无法获得有关SQL Server和索引的信息。我昨晚在桌子上工作,包含100M行。我创建了以下索引:
CREATE NONCLUSTERED INDEX [x_acct_x_date_x_type] ON [mail_master]
(
[letter_acct] ASC,
[letter_date] ASC,
[letter_type] ASC
)
我通常不会创建包含3列的索引。我在此表中用于生产的select语句花费了6秒,其中WHERE子句利用了这3个字段中的每一个。我把我的代码和索引提到了一个同事,他是一个有点老派的优化建议,他建议删掉letter_type。然后我们运行相同的代码,花费6秒,将替换的索引应用于两个字段,现在需要0秒。
我问他为什么,除了我的索引的静态数据大于修改后的索引之外,他无法给我一个答案。他是绝对正确的,但我真的不明白为什么它现在会0秒。
谁能告诉我为什么会这样?提前谢谢。
这是CREATE TABLE语句:
CREATE TABLE [mail_master](
[client_acct] [varchar](4) NULL,
[letter_acct] [varchar](11) NULL,
[letter_date] [datetime] NULL,
[letter_type] [varchar](25) NULL,
[letter_balance] [money] NULL,
[special] [varchar](35) NULL,
[call] [datetime] NULL,
[mail_return] [varchar](1) NULL,
[payment_date] [datetime] NULL,
[post_date] [datetime] NULL,
[promise] [datetime] NULL,
[age] [int] NULL
) ON [PRIMARY]
这是有问题的tsql代码:
DECLARE @ClientTable AS TABLE (
client_acct VARCHAR(4),
client_name VARCHAR(40),
grade VARCHAR(2),
acct_type VARCHAR(20)
)
INSERT INTO @ClientTable (
client_acct,
client_name,
grade,
acct_type
)
SELECT client_acct_info_t.client_acct,
client_name,
grade,
acct_type
FROM client_acct_info_t,
client_master_t
WHERE client_master_t.client_acct = client_acct_info_t.client_acct
AND acct_status = 'A'
SELECT mail_master.client_acct AS 'Client #',
client_name AS 'Client Name',
COUNT(*),
SUM(total_payments) AS 'Total Payments',
SUM(sum_payments) AS 'Total Payment Dollars'
FROM mail_master,
@ClientTable AS ClientTable
WHERE mail_master.client_acct = ClientTable.client_acct
AND letter_date >= '03/01/2014'
AND letter_date <= '03/25/2014'
AND letter_type = 'PRECOLLECT'
AND letter_balance >= 100
AND letter_balance <= 1000
GROUP BY mail_master.client_acct,
client_name
答案 0 :(得分:1)
使用多列索引的关键是查询是所谓的 Sargable ,它来自 S earch Arg ument 能够。多列索引主要按第一列排序,绑定按第二列排序等。
按逻辑顺序,三列索引将按如下方式排序:
first second third
1 1 1
1 1 2
1 1 3
1 2 1
1 5 2
2 1 5
2 2 1
因此,为了寻找索引的特定部分,查询必须具有第一列的值,并且要使用索引中的第二列,它必须具有第一列的精确值。*如果列具有不等式或范围过滤器,则它可以使用该列的索引,但不能用于之后的任何列。
通过查看查询,我们可以看出,如果使用了索引,那么它就是一个完整的扫描,这意味着它并没有真正用作索引。您可以查看执行计划并查找seek vs scan来确定。后续运行会更快,因为数据缓存在内存中,因此不必再次从磁盘读取数据。
查看您的查询,您有client_acct
和letter_type
作为精确比较,因此我会将它们用作前两列,首先是更具选择性的列,所以我认为{{1 }}。对于第三列,我猜测client_acct
会更具选择性,所以我建议。我还会letter_date
索引上的INCLUDE
列,以便它可以过滤不适合的行,即使它无法搜索这些行。此外,SQL可以通过多种方式执行查询,因此这不一定是最好的索引,但我希望它相当不错。
目前尚不清楚letter_balance
和total_payments
的来源,但我会假设它们来自sum_payments
方。在这种情况下,索引是覆盖,这意味着查询可以从索引中获取所需的所有信息,并且永远不需要回顾主表。
*适用于SQL Server。有些RDMS可以使用索引,即使较早的列不精确,但如果可能的话,最好仍然是准确的。