TSQL多列索引

时间:2014-04-25 16:30:52

标签: sql sql-server tsql optimization indexing

我无法获得有关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

1 个答案:

答案 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_acctletter_type作为精确比较,因此我会将它们用作前两列,首先是更具选择性的列,所以我认为{{1 }}。对于第三列,我猜测client_acct会更具选择性,所以我建议。我还会letter_date索引上的INCLUDE列,以便它可以过滤不适合的行,即使它无法搜索这些行。此外,SQL可以通过多种方式执行查询,因此这不一定是最好的索引,但我希望它相当不错。

目前尚不清楚letter_balancetotal_payments的来源,但我会假设它们来自sum_payments方。在这种情况下,索引是覆盖,这意味着查询可以从索引中获取所需的所有信息,并且永远不需要回顾主表。

*适用于SQL Server。有些RDMS可以使用索引,即使较早的列不精确,但如果可能的话,最好仍然是准确的。