索引以检查非常大的表在特定列中是否包含确切的字符串

时间:2018-09-25 04:38:06

标签: sql-server indexing sql-server-2016

我有一个非常大的SQL表(约5亿行)

CREATE TABLE [dbo].[TestDefinition]
(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](500) NOT NULL,
    [TeamId] [int] NOT NULL,

    CONSTRAINT [PK_Test] 
        PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
GO

我想在表中插入新记录,但是首先我需要确保记录不存在。

我实质上是用要插入的值(@TestDefinitionInput-TVP)构建一个表,然后将其传递给存储过程。

WITH W AS 
(
   SELECT [Name], TeamId
   FROM @TestDefinitionInput
),
X AS 
(
   SELECT W.* 
   FROM W
   LEFT JOIN TestDefinition td ON td.TeamId = W.TeamId AND td.[Name] = W.[Name] 
   WHERE td.Id IS NULL
)
INSERT INTO TestDefinition ([Name], [TeamId]) 
   SELECT [Name], [TeamId]
   FROM X;

我的问题是,这确实有5亿行。我确实不是很精通SQL,并且想知道如何(如果应该)索引dbo.[TestDefinition],以使其尽可能快。

1 个答案:

答案 0 :(得分:2)

您有两种可能:

您可以考虑将主键更改为:

CONSTRAINT [PK_Test] 
    PRIMARY KEY CLUSTERED (TeamId ASC, [Id] ASC)

以扩大附加到主键的聚簇索引为代价,您不会在“标识”列上刻录范围搜索索引。这将需要定期清除

另一个(可能首选)选项是创建非聚集索引:

CREATE INDEX IX_TestDefinition_TeamId_Name 
    ON dbo.TestDefinition(TeamId, Name)

[注意:由于它是集群键,因此无需显式包含Id,并且它会添加到每个非集群索引中。]

我还将考虑将传入的TVP参数值放入临时表而不是表变量中,并加入该表(甚至在该表上创建相应的索引)。表变量因基数估计不佳而臭名昭著。

我还将考虑通过页面压缩来添加索引(如果可以的话,还可以添加表格)

.... with (data_compression = page);

Data compression并非在每个版本的SQL Server中都可用。从SQL Server 2016开始,它是标准版。

您应该考虑的另一件事是一次插入50K或100K的批处理,并且之间要短暂睡眠。这样可以防止大量日志文件增长和争用,并使其他进程有机会访问该表。