我有一个具有以下定义的表:
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分钟!
顺便说一句,该表具有无法删除的“聚集的列存储索引”,因为上次尝试删除它时,由于磁盘空间不足,DROP失败。缩小数据库后,存储数据的驱动器有28 GB的可用空间(50 GB可用空间)。
请告知我该怎么做,需要创建哪些索引以及使用哪些设置来优化这些查询和WHY的性能,以便我和其他人可以理解和学习。
此外,如果我还需要在某些WHERE子句中指定DEPARTMENT和SOURCE,该怎么办?这些列只有几个不同的值。
谢谢!
答案 0 :(得分:1)
我认为一个索引就足够了:
create index idx_transactions_timestamp_cid_amt on transactions(timestamp, cid, amt);
这涵盖了第一个查询。索引中的cid
与查询中索引的使用无关。索引将根据日期进行扫描。您可以选择“包含” amt
,而不是将其作为单独的键。
答案 1 :(得分:0)
在Time_Stamp
上创建包含AMT
和CID
的索引应该优化您问题中的查询。
“为什么”是一个广泛的主题,不太适合堆栈溢出的答案。阅读有关SQL Server索引的教程和博客。
编辑评论评论:
您在WHERE子句中进行过滤的列应该是索引本身的一部分(即使它们只有几个不同的值),您刚刚在SELECT列表中返回的列也应该是索引的INCLUDES子句的一部分
答案 2 :(得分:0)
一般的经验法则是为where子句中的列建立索引,而任何连接谓词都是粗略的指南,而不仅仅是狂加应用SSMS的索引提示,它们常常在说谎,因为它们会变得更好
如果您将索引放在TIME_STAMP上,并将该列包括在选择列中,作为包含列或键,则它们对两个查询都将产生积极影响。
例如
CREATE INDEX IX_TEST1 on Transactions (TIME_STAMP) INCLUDE (AMT)
CREATE INDEX IX_TEST1 on Transactions (TIME_STAMP) INCLUDE (CID)
缺点是这些是date和varchar字段,因此索引将变得很大
答案 3 :(得分:0)
您需要在表上创建聚簇索引(通常是主键,该主键可以由一个或多个合并为唯一值的列组成。聚簇索引不必是唯一的,但主键必须是。会猜出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);