假设我有两个简单的表Users和Posts定义如下:
CREATE TABLE [dbo].[Users](
[UserID] [int] IDENTITY(1,1) NOT NULL,
[Username] [varchar](255) NULL,
[FirstName] [varchar](255) NULL,
[LastName] [varchar](255) NULL,
[DateOfBirth] [datetime] NULL,
[Age] [int] NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
[UserID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Posts](
[PostID] [int] IDENTITY(1,1) NOT NULL,
[Content] [varchar](max) NULL,
[NumberOfLikes] [int] NULL,
[UserID] [int] NULL,
[CreateDateUTC] [datetime] NULL,
[Tags] [varchar](max) NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY CLUSTERED
(
[PostID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
显然,当数据库变得非常大时,性能调整将变得必要.我认为Posts中的UserID列非常重要,因为我的大多数查询都是通过它过滤的.所以我认为我需要在该列上定义一个INDEX.
至于覆盖字段,我们假设,为了这个问题的目的,我的所有查询看起来都是一样的(除了WHERE部分):
SELECT
Posts.Content
,Posts.NumberOfLikes
,Users.UserName
FROM
Posts
INNER JOIN
Users
ON
Posts.UserID = Users.UserID
WHERE
Posts.UserID = @UserID;
我的问题是关于覆盖领域.我可以轻松定义一个涵盖Content和NumberOfFields的索引,如下所示:
CREATE NONCLUSTERED INDEX [IX_Posts_UserID] ON [dbo].[Posts] (UserID) INCLUDE (Content, NumberOfLikes)
但是,我的查询始终与Users表连接.那么我的索引仍然相关(在性能方面)尽管我的查询包含更多字段(来自另一个表)而不是索引的覆盖字段?我知道我无法覆盖另一个表中的字段,所以在这种情况下如何优化查询?当我查看执行计划时,我看到我的索引IX_Posts_UserID实际上已被使用(50%,另外50%由PK_Users使用)但我很困惑,因为我选择的列不是由指数.
所以这里的最终问题是:表如何加入决定SQL Server是否使用索引的因素?甚至更简单,联接如何影响索引?
最佳答案 通常,建议至少在所有外键上添加非聚簇索引,因为它们可能经常用于JOIN操作(偶尔在WHERE谓词中).
要在此处专门讨论您的情况,您选择创建的索引包含VARCHAR(MAX)字段,这将影响SQL Server决定使用它的方式.由于VARCHAR(MAX)理论上可以增长到包含2GB的数据,因此引擎不会在页面级别存储字段数据,因为它限制为8KB.在这种情况下,SQL Server认为最便宜的操作是扫描索引(顺便说一句,这并不总是坏事,特别是如果选择性很高).
我在这里建议,保持索引紧密并将其限制在UserId字段以促进连接的性能.我不一定会担心您的内容列的覆盖索引,因为引擎需要深入挖掘此数据的页面级别.
create nonclustered index ix_posts_userid on dbo.Posts (UserID);
请记住,索引不是魔术,绝对不是所有性能问题的银弹.设计合理,可以降低系统的效率.想想“办公室里的行政人员”,他们花钱雇佣.但是在效率方面为业务增加价值.
As a complete aside, please do not store tags as a comma-separated list, which it seems like you’re doing here.
相反,将标签存储为共享资源并通过“连接表”进行链接.
create table Tags (
TagId int identity primary key
,Content nvarchar(128) not null -- or whatever width suits your needs
);
create table PostTags (
PostId int not null
,TagId int not null
,primary key (PostId, TagId)
);