创建索引时如何选择列?

How to choose columns when creating index?

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

在网站中获取用户的查询是:

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

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

获取用户 posts 的查询是:

Select * from Posts where UserID='something'

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

我想说的是:

时如何选择列
  1. 正在创建聚簇索引。
  2. 正在创建非聚集索引。
  3. 创建包含列的非集群。

上面的例子只是为了说明题意。目标不是为示例中的两个查询找到一个好的索引,而是想出一个可以用来帮助创建索引时选择列的基础。

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

因此您可以在用户 table 以及电子邮件和密码上使用非聚集索引。

差不多这个索引:

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,它们不包含在您的索引中,因此,您的查询将执行 Key Lookup(它将在创建的索引中查找记录并回头查看您的 dbo.User table 来查找 SELECT 语句(用户 ID 和用户名)的匹配值。为避免这种情况,您可以使用 INCLUDED 列创建索引以删除键查找(您会想要这样做)。

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

使用这个索引,您将在您的执行计划中有一个很好的 NON CLUSTERED INDEX 查找。

此外,选择索引列的顺序很重要。比方说,您的 table 将包含 UserTypeID(数量不多)。因此,您将传递一些特定的 UserTypeID 和 UserID 列表,然后 SQL 服务器可能想要选择一个索引,该索引将 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;

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

现在,如果我们指定 UserId,它将查找您的聚簇索引(我们将 UserId 作为键):

SELECT *
FROM #Users
WHERE UserID = 602;

现在让我们创建不包含列的索引并查询一些内容:

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

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

正如我所说,它使用创建的索引并进行密钥查找,它找到匹配的电子邮件和密码,并找到 table 中的其余列以输出它们(P.S。如果您要输出,比方说,只有电子邮件,它不会进行密钥查找,也不需要):

现在让我们使用包含的用户名和上面的 运行 查询创建索引。正如我之前告诉您的那样,它将使用普通的非聚集索引查找生成这个很好的执行计划:

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

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

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