【问题标题】:What indexes should I create on this table in SQL Server?我应该在 SQL Server 的这个表上创建什么索引?
【发布时间】:2018-07-05 14:10:34
【问题描述】:

我有一个具有以下定义的表:

CREATE TABLE [dbo].[Transactions]
(
    [ID] [varchar](18) NOT NULL,
    [TIME_STAMP] [datetime] NOT NULL,
    [AMT] [decimal](18, 4) NOT NULL,
    [CID] [varchar](90) NOT NULL,
    [DEPARTMENT] [varchar](4) NULL,
    [SOURCE] [varchar](14) NULL,
    PRIMARY KEY NONCLUSTERED 
    (
        [ID] ASC
    )
    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

该表中有 7500 万行。不知何故,它占用了 20 GB 的磁盘空间!

以下2个查询...

SELECT 
    SUM(AMT) 
FROM 
    Transactions
WHERE 
    TIME_STAMP >= '2017-11-11 00:00:00' AND 
    TIME_STAMP < '2017-11-12 00:00:00'


SELECT 
    COUNT(DISTINCT(CID))
FROM 
    Transactions
WHERE 
    TIME_STAMP >= '2017-11-11 00:00:00' AND 
    TIME_STAMP < '2017-11-12 00:00:00'

...每个运行大约需要 2 分钟!

顺便说一句,该表有一个我无法删除的“Clustered ColumnStore Index”,上次我尝试删除它时,由于磁盘空间不足,DROP 失败。存储数据的驱动器有 28 GB 可用空间,压缩 DB 后没有 50 GB。

请告知我需要做什么,我需要创建哪些索引,使用哪些设置来优化这些查询的性能以及为什么这样我和其他人可以理解和学习。

另外,如果我还需要在某些 WHERE 子句中指定 DEPARTMENT 和 SOURCE 怎么办?这些列只有几个不同的值。

谢谢!

【问题讨论】:

  • @Larnu - 已发布问题。
  • 查看 SSMS 中这两个查询的实际执行计划,它们通常还包括索引提示。
  • 查看您的查询,我会查看时间戳上的分区。

标签: sql sql-server database indexing


【解决方案1】:

我认为一个索引就足够了:

create index idx_transactions_timestamp_cid_amt on transactions(timestamp, cid, amt);

这涵盖了第一个查询。索引中的cid 与查询使用索引无关。将根据日期扫描索引。您可以选择“包含”amt,而不是将其作为单独的键包含。

【讨论】:

  • 所以,我帖子中的查询要快得多,但是如果我添加另一个 WHERE 子句,例如 WHERE DEPARTMENT = 'ABC' ,那么我们又回到了糟糕的性能。
  • @Infin8Loop 。 . .您需要为该查询使用不同的索引。此建议回答了您实际提出的问题。
  • 那么我需要什么索引才能使我的所有查询(包括 DEPARTMENT 和 SOURCE 列上的 WHERE 条件)都快?
  • 顺便说一句,DEPARTMENT 和 SOURCE 列中只有几个不同的值。
【解决方案2】:

一般的经验法则是索引 where 子句和任何连接谓词中的列,但这是一个粗略的指南,不要只是广泛应用 SSMS 中的索引提示,它们通常会撒谎,因为它们会更好

如果您在 TIME_STAMP 上放置索引并将选择中的列作为包含列或键包含在内,那么它们应该对两个查询都有积极影响。

例如

CREATE INDEX IX_TEST1 on Transactions (TIME_STAMP) INCLUDE (AMT)
CREATE INDEX IX_TEST1 on Transactions (TIME_STAMP) INCLUDE (CID)

缺点是这些是日期和 varchar 字段,因此索引会变得相当大

【讨论】:

  • 为什么不包含两个列的单个索引?
  • 你可以做我只是为两个查询做一个“理想的”作为单独的索引
  • 如果我还需要在某些 WHERE 子句中指定 DEPARTMENT 和 SOURCE 怎么办?这些列只有几个不同的值。
  • 您可以将它们添加为包含列或键列。如果您选择它作为关键列,则将它们作为最右边的列,因为它的选择性最低。包含的列未排序,而是将该列添加到索引中,因此它不需要进行查找等...使用set statistics io on 并检查查询对不同索引所做的工作量。当您开始时,会有很多线索和错误
【解决方案3】:

您的表需要一个聚集索引(通常是主键,它可以由一个或多个组合成唯一值的列组成。聚集索引不必是唯一的,但主键可以。我会猜测 id 是主键的一个很好的候选者。然后我会根据 time_stamp 创建一个索引并包括 CID 和 Amt。

您希望查询使用查找(这意味着 SQL Server 仅读取相关行)而不是扫描(SQL Server 读取整个表)。

再一次,我们没有看到数据,但这是我的建议:

alter table [dbo].[Transactions] add constraint PK_transactions_id primary key ( id);
create index ix_transactions_time_stamp_amt on transactions(timestamp) include (cid,amt);

【讨论】:

  • 如果我还需要在某些 WHERE 子句中指定 DEPARTMENT 和 SOURCE 怎么办?这些列只有几个不同的值。
【解决方案4】:

Time_Stamp 上创建包含 AMTCID 的索引应该优化您问题中的查询。

“为什么”是一个广泛的主题,不太适合堆栈溢出的答案。阅读有关 SQL Server 索引的教程和博客。

编辑对评论的回应:

您在 WHERE 子句中过滤的列应该是索引本身的一部分(即使它们只有几个不同的值),您刚刚在 SELECT 列表中返回的列应该是索引的 INCLUDES 子句的一部分.

【讨论】:

  • 如果我还需要在某些 WHERE 子句中指定 DEPARTMENT 和 SOURCE 怎么办?这些列只有几个不同的值。
猜你喜欢
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-10
  • 2011-08-24
  • 2016-08-25
  • 1970-01-01
相关资源
最近更新 更多