sql – 联接如何影响使用的索引?

假设我有两个简单的表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是否使用索引的因素?甚至更简单,联接如何影响索引?

编辑:Per Simonare的评论,下面是执行计划:
《sql – 联接如何影响使用的索引?》

最佳答案 通常,建议至少在所有外键上添加非聚簇索引,因为它们可能经常用于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)
);
点赞