.net 如何提高SQL Server存储过程的性能?
我通过企业库DAL应用程序块调用一个存储过程,并在我的过程中传递一个数据表,该数据表作为自定义表数据类型(.net 如何提高SQL Server存储过程的性能?,.net,sql-server,performance,stored-procedures,duplicates,.net,Sql Server,Performance,Stored Procedures,Duplicates,我通过企业库DAL应用程序块调用一个存储过程,并在我的过程中传递一个数据表,该数据表作为自定义表数据类型(@namesasNamesTable)被“接收”。从第二次调用开始,这个过程非常慢,我正在寻找一种不同的方法来实现它,这样性能会大大提高 Names/HistoricalNames表非常庞大(1亿条记录),传递给这些表的数据(通过dataset/table参数)大约有400万条记录) 它的基本功能(需要做的)如下: create table Names ( id int IDENTI
@names
asNamesTable
)被“接收”。从第二次调用开始,这个过程非常慢,我正在寻找一种不同的方法来实现它,这样性能会大大提高
Names/HistoricalNames表非常庞大(1亿条记录),传递给这些表的数据(通过dataset/table参数)大约有400万条记录)
它的基本功能(需要做的)如下:
create table Names
(
id int IDENTITY(1,1) NOT NULL,
name nvarchar(20),
otherId uniqueidentifier
)
create table HistoricalNames
(
id int IDENTITY(1,1) NOT NULL,
name nvarchar(20),
otherId uniqueidentifier
)
create table NameTable
(
name nvarchar(20)
otherId uniqueidentifier
)
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_ImportNames]
@names NameTable READONLY
AS
BEGIN
IF ((SELECT COUNT(cd.name) FROM Names as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
BEGIN
SELECT 2;
END
ELSE IF ((SELECT COUNT(cd.name) FROM HistoricalNames as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
BEGIN
SELECT 2;
END
ELSE
BEGIN
INSERT INTO Names (name, otherId) SELECT * FROM @names;
SELECT 1;
END
END
GO
@names
(这是DataTable/Table参数- 检查
或名称
表是否包含新数据集/表参数中包含的任何名称,如果包含,则跳过整个导入并返回2历史名称
- 否则,在
名称中插入
中的所有记录,并返回1@names
create table Names
(
id int IDENTITY(1,1) NOT NULL,
name nvarchar(20),
otherId uniqueidentifier
)
create table HistoricalNames
(
id int IDENTITY(1,1) NOT NULL,
name nvarchar(20),
otherId uniqueidentifier
)
create table NameTable
(
name nvarchar(20)
otherId uniqueidentifier
)
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_ImportNames]
@names NameTable READONLY
AS
BEGIN
IF ((SELECT COUNT(cd.name) FROM Names as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
BEGIN
SELECT 2;
END
ELSE IF ((SELECT COUNT(cd.name) FROM HistoricalNames as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
BEGIN
SELECT 2;
END
ELSE
BEGIN
INSERT INTO Names (name, otherId) SELECT * FROM @names;
SELECT 1;
END
END
GO
表值参数(@names
)如下所示:
create table Names
(
id int IDENTITY(1,1) NOT NULL,
name nvarchar(20),
otherId uniqueidentifier
)
create table HistoricalNames
(
id int IDENTITY(1,1) NOT NULL,
name nvarchar(20),
otherId uniqueidentifier
)
create table NameTable
(
name nvarchar(20)
otherId uniqueidentifier
)
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_ImportNames]
@names NameTable READONLY
AS
BEGIN
IF ((SELECT COUNT(cd.name) FROM Names as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
BEGIN
SELECT 2;
END
ELSE IF ((SELECT COUNT(cd.name) FROM HistoricalNames as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
BEGIN
SELECT 2;
END
ELSE
BEGIN
INSERT INTO Names (name, otherId) SELECT * FROM @names;
SELECT 1;
END
END
GO
这是一个过程:
create table Names
(
id int IDENTITY(1,1) NOT NULL,
name nvarchar(20),
otherId uniqueidentifier
)
create table HistoricalNames
(
id int IDENTITY(1,1) NOT NULL,
name nvarchar(20),
otherId uniqueidentifier
)
create table NameTable
(
name nvarchar(20)
otherId uniqueidentifier
)
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_ImportNames]
@names NameTable READONLY
AS
BEGIN
IF ((SELECT COUNT(cd.name) FROM Names as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
BEGIN
SELECT 2;
END
ELSE IF ((SELECT COUNT(cd.name) FROM HistoricalNames as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
BEGIN
SELECT 2;
END
ELSE
BEGIN
INSERT INTO Names (name, otherId) SELECT * FROM @names;
SELECT 1;
END
END
GO
这可以很容易地调整性能吗?任何帮助都将不胜感激!打开实际执行计划显示-这将显示性能更差的地方。可能是这样的:
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_ImportNames]
@names NameTable READONLY
AS
BEGIN
IF EXISTS(SELECT NULL FROM Names as cd WHERE EXISTS(SELECT NULL FROM @names as c WHERE c.name=cd.name))
BEGIN
SELECT 2;
END
ELSE IF EXISTS(SELECT NULL FROM HistoricalNames as cd WHERE EXISTS(SELECT NULL FROM @names as c WHERE c.name=cd.name))
BEGIN
SELECT 2;
END
ELSE
BEGIN
INSERT INTO Names (name, otherId) SELECT * FROM @names;
SELECT 1;
END
END
嗯
- 一个语句“IF NOT EXISTS”和两个CEHCK。您每次都计算完整计数,但只关心是否存在一个项,这可以更快地完成(找到一行后放弃查询)。EXISTS子句的存在就是因为这个原因
- 表值参数几乎肯定是您的问题
对于一个基本的ETL过程来说,使用表参数似乎很重要,但在任何情况下,表值参数都不会被索引
因此,您将得到一个4m行的表扫描,这在关系数据库中是您永远不希望看到的
通过将它插入一个真正的表中作为一个带有索引的暂存区域,然后对该表而不是参数执行操作,您应该可以获得巨大的提升。此外,还要确保在其他表上也有索引。抛开传递这么多数据听起来是个坏主意的问题,作者建议的方法是Arion是我的建议。假设您在名称列上有索引,您不需要任何有关匹配名称或匹配位置的详细信息,您只想找到第一个匹配项并返回您成功的消息 我还将使用联接检查exists的性能:
if exists(select 1
from Names exist
inner join @names newNames on newNames.name = exist.name)
begin
select 2;
end
另请注意,对于“不匹配”情况,通常建议在插入时明确使用列名:
insert into Names (name, otherId)
select name, otherId
from @names
你在这些表上有什么样的索引?请注意。我们在
name
列上有索引,但是这些没有被纳入“创建脚本”中自动…并且您在id
列上有群集主键,对吗?是的,但是在表类型参数中不是这样的…我如何有效地组合这些键?只需使用一个没有参数的简单联接?如果找到重复项,此查询非常有效。但是,如果没有找到重复项,它仍然会挂起超过一个小时在尝试插入50.000条记录(其中Names表包含100.000条记录)时,需要120秒…这不是正确的解决方案吗?如何显示“活动”存储过程的执行计划?它是从.NET调用的,数据表是从.NET传递的…从SSMS运行存储过程,或者如果您没有访问权限,请让dba执行此操作-计划在那里可见。将其选择为变量(temp)是否有帮助表?或者它应该是一个真实的表吗?@ RePosibe我只使用一个真实的表。我也可能考虑一下你的整个体系结构,你将4m行发送到一个PROC,而不是一个传统的ETL过程。这只是一个非常罕见的场景。我们正在从CSV文件中提取数据,并把它加载到一个DATATATE中,它被发送到过程。数据库和应用程序服务器是分布式的,不能将CSV发送到数据库服务器。是否有另一种解决方案可以将整个CSV文件视为“批处理”?@ReFocus我不知道您的架构或动机。如果不可能,那没关系。我们在数据仓库中通常做的是将压缩文件获取到SSIS服务器,然后将其加载到数据库中的暂存表中。该过程是否使用内联查找或稍后的批处理SQL操作将取决于设计。这只是一点通过proc table参数接口发送4m行是很不寻常的,正如您所看到的,它没有索引。如果您需要重试批处理,您现在必须通过网络再次发送4m行。