我正在通过企业库DAL应用程序块调用存储过程,并在我的过程中传递一个DataTable,后者又被“接收”为自定义表数据类型(@names
为NamesTable
)。从第二次调用开始,该过程非常慢,我正在寻找一种不同的方式来实现它,因此性能大大提高。
Names / HistoricalNames表格很大(1亿条记录),传递给这些表格的数据(通过数据集/表格参数)大约有400万条记录。
基本上它的作用(需要做的)如下:
@names
(这是DataTable / Table参数Names
或HistoricalNames
表是否包含新数据集/表参数中包含的任何名称,如果是,则跳过整个导入并返回2 @names
中插入Names
的所有记录并返回1 ; 表格如下所示:
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
)
表值参数(@names
)如下所示:
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
这可以轻松调整性能吗?任何帮助将不胜感激!
答案 0 :(得分:3)
表值参数几乎肯定是你的问题。
Table Valued Parameter has slow performance because of table scan
基本的ETL过程使用表参数似乎相当多,但无论如何,表值参数都没有编入索引。
所以你得到一个4米的行表扫描,这绝不是你想在关系数据库中看到的。
你应该通过将它作为带有索引的临时区域插入到REAL表中然后在该表而不是参数上进行操作来获得巨大的提升。另外,请确保在其他表上也有索引。
答案 1 :(得分:2)
也许是这样的:
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
答案 2 :(得分:1)
打开实际的执行计划显示 - 这将显示性能更差的地方。
答案 3 :(得分:1)
Hm中
答案 4 :(得分:1)
抛开那些传递大量数据的问题听起来不错,Arion建议的方法就是我所建议的。您不需要有关哪些名称匹配或他们在何处执行的任何详细信息,因此假设您在名称列上有索引,您只想找到第一个匹配项并返回您成功的名称。
我还会使用连接检查存在的性能:
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