如何在创建索引时选择列?

时间:2015-07-03 12:23:01

标签: sql-server indexing

这似乎是一个奇怪的问题。我知道sql server中的不同类型的索引(集群,非集群,唯一,过滤,包含列的索引......等),我知道如何创建它们。另外我知道索引取决于查询,但我不知道在创建索引时谁选择列。例如,假设一个允许用户发布文本和图像的简单网站。该网站有一个简单的两个表格,如图所示:

How to choose columns when creating index

获取网站用户的查询是:

Select UserID,UserName from User where Email='something' and Password='something'

假设我想为这个表创建索引,我应该在创建索引时包含哪些列?我知道不同类型的索引可能包含不同的列,但我可以决定何时创建聚簇或非聚簇应选择哪些列。我看到一些索引的例子几乎总是在where子句之后选择列。这是真的吗?

获取用户帖子的查询是:

Select * from Posts where UserID='something'

此查询与第一个查询不同。此查询可能返回多行,而第一行将始终返回一行。现在同样的问题,如何选择列?

我想说的是如何在以下时间选择列:

  1. 创建聚集索引。
  2. 创建非聚集索引。
  3. 使用包含的列创建非群集。
  4. 上面的例子只是为了说明问题的想法。目标不是为示例中的两个查询找到一个好的索引,而是想出一个可以在创建索引时帮助选择列的基础。

2 个答案:

答案 0 :(得分:25)

在完美世界中,您希望对列在WHERE子句或JOIN条件中的列进行索引。在您的情况下,它将是EmailPassword列。

因此,你可以在用户表和电子邮件和密码上找到非聚集索引。

这个指数差不多:

CREATE NONCLUSTERED INDEX idx_User_Email_Password
    ON dbo.User (Email, Password);

因此,如果您将运行此查询:

SELECT UserID, UserName
FROM User
WHERE Email = 'something'
    AND Password = 'something';

您将最终使用刚刚创建的索引(最有可能)或聚集索引,它将寻求通过它。但是,您的查询选择了未包含在索引中的UserID和UserName,因此,您的查询将执行密钥查找(它将在创建的索引中查找记录,并将回顾您的dbo.User表查找SELECT语句的匹配值(UserID和UserName)。为避免这种情况,您可以使用INCLUDED列创建索引以删除密钥查找(您可能希望这样做)。

CREATE NONCLUSTERED INDEX idx_User_Email_Password
    ON dbo.User (Email, Password)
    INCLUDE (UserID, UserName);

使用此索引,您的执行计划中将有一个很好的非聚集索引查找。

此外,选择索引列的顺序。比方说,你的表将包含UserTypeID(它们中没有很多)。因此,您将传递一些特定的UserTypeID和一个UserID列表,然后SQL Server可能希望选择一个索引,其中UserTypeID作为第一个索引列。

所以有些测试:

CREATE TABLE #Users
(
    UserId INT
    , UserName VARCHAR(500)
    , Email VARCHAR(500)
    , Password VARCHAR(500)
);

CREATE CLUSTERED INDEX idx_Users_UserID
    ON #Users (UserID);

-- Some test data from my DB
INSERT INTO #Users (UserId, UserName, Email, Password)
SELECT TOP (10000) UserId, UserName, Email, 'password'
FROM Users;

所以这是查询:

SELECT *
FROM #Users;

这将执行索引扫描,因为我们没有指定任何细节。 enter image description here

现在,如果我们指定UserId,它将搜索您的Clustered索引(我们将UserId作为键):

SELECT *
FROM #Users
WHERE UserID = 602;

enter image description here

现在让我们创建索引而不包含列并查询:

CREATE NONCLUSTERED INDEX idx_Users_Email_Password
    ON #Users (Email, Password);

SELECT *
FROM #Users
WHERE Email = 'k0641088@kingon.a.uk';

正如我所说,它使用创建的索引并执行密钥查找,它找到匹配的电子邮件和密码,并找到表中的其余列以输出它们(PS如果您要输出,请说,只有电子邮件,它不会进行密钥查找,也不需要): enter image description here

现在让我们使用包含的UserName创建索引并运行上面的查询。正如我之前告诉你的那样,它将使用简单的NonClustered Index搜索产生这个很好的执行计划:

CREATE NONCLUSTERED INDEX idx_Users_Email_Password_iUserName
    ON #Users (Email, Password)
    INCLUDE (UserName);

enter image description here

这是一篇高质量的文章,我建议阅读它:https://www.simple-talk.com/sql/performance/index-selection-and-the-query-optimizer/

答案 1 :(得分:0)

我更愿意 在电子邮件上创建非聚集索引,密码可以是包含的列 并在UserId上创建一个聚簇索引,这可能是一个自动增量列