0

我有一个通常的 sqlserver 表

intID(primary key),field1,field2,manyotherfields..., datetime TimeOperation

TimeOperation BETWEEN startTime AND endTime我 99% 的不同类型的查询都以, 然后,开头select * (or count(*)) where fieldA=xxx,并与其他较小的表连接。 select *因为或多或少我需要所有的领域。

我显然在...上创建了一个索引,TimeOperation但性能不够好,所以我想添加一些索引键列或索引包含列,但我有点困惑。

我知道两者之间的区别,但我不知道在每种情况下添加一列对速度和大小的影响有多大。

我想最大的改进是创建一个包含所有列的索引,对吗?(但在空间方面我买不起)

如果我经常使用field1=xxx例如,添加field1到索引键列(之后TimeOperation)会提供更好的性能,对吗?

另外......只是为了确定包含列的索引如何工作:如果我选择TimeOperation一定范围内的行,sql会为我感兴趣的行寻找我的 TimeOperation 索引,它比扫描所有表更快,因为在索引中 TimeOperation 值是按升序排列的,对吗?但后来我需要所有数据,现在我需要这些行的所有其余数据字段...... sql 如何检索数据?我猜它对索引中的那些行有一种书签,对吧?但是它必须多次点击表......所以包括索引中的所有列将节省点击表的时间,对吗?

谢谢!马蒂亚

4

2 回答 2

1

我们将需要有关您的查询表示例的更多信息来完全解决这个问题,但是:

  • DateTime 列本身应该具有高度选择性,因此以TimeOperation第一列作为索引的索引应该解决针对TimeOperation.
  • 不要盲目地将所有列添加到索引中,甚至不要添加到包含的索引中 - 这会使索引页面密度变差并且适得其反(您将在索引中复制表)。
  • 如果您的数据库中的所有数据都以 为中心TimeOperation,您可能会考虑围绕它构建聚集索引。
  • 如果您有查询,field1 = x那么您需要一个单独的索引field1(假设它具有适当的选择性),即TimeOperation如果它不在查询的 WHERE 子句中,则在索引上没有。
  • 是的,你是对的,当 SQL 在索引中找到一条记录时,它需要在集群中进行键(或 RID)查找以检索其余列。如果您的非聚集索引在您的语句中包含其他列select,则可以避免查找。但由于您使用的是 SELECT(*),因此覆盖索引不太可能有帮助。

编辑

解释 - 选择性和密度在这里详细解释。例如,如果您的查询仅TimeOperation返回少量行(经验法则是 < 5%,但并非总是如此),是否会使用索引,即您的查询具有足够的选择性,让 SQL 可以在TimeOperation.

基本出发点是:

CREATE TABLE [MyTable]
(
  intID INT ID identity(1,1) NOT NULL,
  field1 NVARCHAR(20),
  -- .. More columns, which may be selected, but not filtered 
  TimeOperation DateTime,

  CONSTRAINT PK_MyTable PRIMARY KEY (IntId)
);

基本指标为

CREATE NONCLUSTERED INDEX IX_MyTable_1 ON [MyTable](TimeOperation);
CREATE NONCLUSTERED INDEX IX_MyTable_2 ON [MyTable](Field1);

聚类考虑/选项

如果您的大部分记录以“串行”升序 TimeOperation 顺序插入,即 intId 和 TimeOperation 将同时增加,那么我会将集群保留在 intID 上(默认值)(即表 DDL 是PRIMARY KEY CLUSTERED (IntId),无论如何这是默认值)。

但是,如果和之间没有相关性,并且如果您的大多数查询都是这种形式,则(并将 PK 更改为将避免书签查找)。更好的是,如果保证 的值是唯一的,那么将提高密度,因为它将避免使用唯一符。IntIdTimeOperationSELECT * FROM [MyTable] WHERE TimeOperation between xx and yyCREATE CLUSTERED INDEX CL_MyTable ON MyTable(TimeOperation)PRIMARY KEY NONCLUSTERED (IntId)TimeOperationCREATE UNIQUE CLUSTERED INDEX CL_MyTable ON MyTable(TimeOperation)

注意- 对于这个答案的其余部分,我假设你IntIdTimeOperationsARE 高度相关,因此聚类是 by IntId

覆盖索引

正如其他人所提到的,您的使用SELECT (*)是不好的做法,尤其是覆盖索引没有任何用处(例外是COUNT(*))。如果您的查询不是 SELECT(*),而是例如

SELECT TimeOperation, field1
FROM 
WHERE TimeOperation BETWEEN x and y -- and returns < 5% data.

然后更改您的索引TimeOperation以包含field1

CREATE NONCLUSTERED INDEX IX_MyTable ON [MyTable](TimeOperation) INCLUDE(Field1);

或将两者都添加到索引中(首先使用最常见的过滤器,或者如果始终存在两个过滤器,则首先使用最有选择性的过滤器)

CREATE NONCLUSTERED INDEX IX_MyTable ON [MyTable](TimeOperation, Field1);

要么将避免摆脱/关键查找。第二个 (,) 选项将解决您在 WHERE 或 HAVING 子句中过滤 TimeOperation 和 Field1 的查询。

Re: (TimeOperation, Field1) 上的索引和单独的索引有什么区别?

例如

CREATE NONCLUSTERED INDEX IX_MyTable ON [MyTable](TimeOperation, Field1);

对查询没有用

SELECT ... FROM MyTable WHERE Field1 = 'xyz';

该索引仅对具有 TimeOperation 的查询有用

SELECT ... FROM MyTable WHERE TimeOperation between x and y;

或者

SELECT ... FROM MyTable WHERE TimeOperation between x and y AND Field1 = 'xyz';

希望这可以帮助?

于 2012-09-14T14:40:59.447 回答
0

最基本的索引在幕后创建了一层“超树”结构,这允许 SQL 引擎更容易地为索引列找到具有特定值的行。每个索引都创建了一种不同的方式来使用二进制搜索(logN 性能)“深入”到表的数据中。您添加的每个索引都会使该索引的选择速度更快,但会降低插入/更新速度(必须放入数据,然后必须创建索引)。

因此,通常应该为通常用于过滤记录的列组合创建索引。我确实会在 TimeOperation 和 TimeOperation 上单独创建一个索引。

永远不要简单地创建一个包含表的所有列的索引,尤其是像这样的宽列。

于 2012-09-14T14:42:38.497 回答