我有一个非常大的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]
,以使其尽可能快。
答案 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的批处理,并且之间要短暂睡眠。这样可以防止大量日志文件增长和争用,并使其他进程有机会访问该表。