【问题标题】:TSQL Multi Column IndexTSQL 多列索引
【发布时间】:2014-06-11 11:35:18
【问题描述】:

关于 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 列的索引。我从这个表中选择语句用于生产需要 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

【问题讨论】:

  • 第一种可能是你在比较苹果和橙子,因为你需要在没有缓存的情况下比较结果。更重要的是,我们不能在没有看到查询的情况下评论索引的使用。请编辑问题以包含查询。
  • where子句letter_acct引用的哪里?
  • 好问题,在这个特定的查询中,我没有使用 letter_acct 列。

标签: sql sql-server tsql optimization indexing


【解决方案1】:

使用多列索引的关键是让查询成为所谓的Sargable,它来自SArg 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 作为精确比较,所以我会将它们用作前两列,第一列更具选择性,所以我认为 client_acct。对于第三列,我猜letter_date 会更有选择性,所以我建议这样做。我还将INCLUDE 索引上的letter_balance 列,以便它可以过滤不适合的行,即使它无法查找这些行。此外,SQL 可以通过多种方式执行查询,因此这不一定是最好的索引,但我希望它相当不错。

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

*适用于 SQL Server。即使较早的列不准确,一些 RDMS 也可以使用索引,但如果可能,最好还是准确。

【讨论】:

  • 这不仅解释了我最初的问题,还有助于回答 Der U 的问题。 client_acct 实际上只是 letter_acct 的前 4 位数字。即使我没有专门使用 letter_acct (索引列之一),数据也已经排序以使其执行时间为 0。谢谢。
猜你喜欢
  • 2011-10-03
  • 2013-11-04
  • 2011-11-07
  • 2010-09-15
  • 1970-01-01
  • 2018-07-27
  • 2011-03-20
  • 1970-01-01
相关资源
最近更新 更多