2013-03-19 48 views
0

我使用DataAdapter批次到插入到多对多表 批量大小= 1000插入使用存储的过程是很慢的

我有3个表

  1. 学校(ID,姓名)
  2. 学生(ID,姓名)
  3. SCHOOL_STUDENT(学校ID,student_id数据)

我试图围绕700K行插入表中SCHOOL_STUDENT但它是非常缓慢的 我传递的校名和学生姓名存储过程

(
@schoolName varchar(100), 
@studentName varchar(50) 
) 

AS 
BEGIN transaction 

    declare @scoolId int,@studentId int 

    set @scoolId = (select ID from SCHOOL where [SCHOOL_NAME] = @schoolName) 
    set @studentId = (select ID from STUDENT where STUDENT_NAME = @studentName) 

    INSERT INTO [dbo].SCHOOL_STUDENT 
        (SCHOOL_ID,STUDENT_ID) 
      VALUES 
        (@scoolId,@studentId) 

commit transaction 

但这需要约1小时运行。 我怎么能加快这一点, 因为我不知道school_Id既不提前,那么我必须在存储过程中始终选择它们。 (有没有更好的方法)

申请流程是先插入所有学生,然后插入所有学校,然后将它们链接到表school_student中。

+1

您的学校和学生表可能需要一些索引。可能是SCHOOL.Id,SCHOOL.SCHOOL_NAME和STUDENT.ID,STUDENT.NAME中的一个。 – Romoku 2013-03-19 01:10:31

+0

看起来您没有在“SCHOOL”表上设置索引。 – Dai 2013-03-19 01:13:35

回答

3

在效率/影响和精力秩序,也许尝试:

1 - 检查你正在阅读的你的表建立索引。确保每个表上的ID和名称列上的索引。

2-重构你的存储过程是这样的:

INSERT INTO dbo.School_Student(School_ID, Student_ID) 
    SELECT SC.ID, ST.ID 
    FROM dbo.School AS SC 
    JOIN dbo.Student AS ST ON ST.Student_Name = @studentName 
          AND SC.School_Name = @schoolName; 

3-所有的学生证和学生证在调用这个PROC之前从PROC

4-预紧删除交易。循环并传递ID。

5-调查SQL批量复制操作。

+0

我不能使用批量复制 .I'm现在测试TVP执行的900个000行,则UPSERT传递DataTable添加到存储过程 什么,我搜索了近2小时,以找到一种方法,使用bulkCopy,传递表存储过程然后使用合并。或者我在错误的方向看。 – Maro 2013-03-19 23:44:58

0

程序本身是相当不错的。问题在于你将其称为1000次,在那里你会得到性能成本,因为它将每个通话连接断开。

修改过程以接受xml或分隔的语法,每个批处理可以传递大约100到500个。

将其首先存储到登台表(虚拟,但物理),如果在批插入过程中发生了某些情况,它不会损害事务/主表。储存学校名称和学生姓名,在插入时不要做任何选择。

最后,执行一个存储过程,将所有记录从临时表插入到事务/主表中。使用从具有较大记录的表中选择比执行1乘1选择更好。

不要忘记重新验证从登台到交易/主表做插入之前的数据,检查空(未找到)的数据,重复数据,损坏的数据由于字符串分隔符或等

最后,正如您在评论中提到的那样,为了获得更好的性能,请执行一些索引和性能调整。如果处理正确,它应该给你少于10分钟的执行时间。

2

您应该在学生表和学校表上创建索引来优化查找。我也会将你的数据放到一个表中,并使用C#中的SqlBulkCopy来上传它。存储过程可以转换数据并插入密钥。

CREATE PROCEDURE spSchoolStudentTransform 
AS 
BEGIN 

    INSERT INTO [dbo].[School_Student](School_Id, Student_Id) 
    SELECT School.Id, Student.Id FROM SchoolStudent ss 
    JOIN School 
    ON School.School_Name = ss.SchoolName 
    JOIN Student 
    ON Student.Student_Name = ss.StudentName; 

    TRUNCATE TABLE SchoolStudent; 
END 
GO 

CREATE TABLE SchoolStudent 
(
    Id INT IDENTITY PRIMARY KEY 
    ,StudentName VARCHAR(50) NOT NULL 
    ,SchoolName VARCHAR(100) NOT NULL 
); 
GO 

CREATE NONCLUSTERED INDEX ixStudentIdStudentName 
ON Student (Id, Student_Name); 
GO 

CREATE NONCLUSTERED INDEX ixSchoolIdSchoolName 
ON School (Id, School_Name); 
GO 

using (var connection = new SqlConnection(connectionString)) 
{ 
    using(var sqlBulkCopy = new SqlBulkCopy(connection)) 
    { 
     sqlBulkCopy.DestinationTableName = "SchoolStudent"; 
     sqlBulkCopy.EnableStreaming = true; 
     sqlBulkCopy.BatchSize = 1000; 

     sqlBulkCopy.WriteToServer(dataReader); 
    } 
} 
+0

在您的C#代码中,您只将结果复制到临时表SchoolStudent中,但是在哪里调用spSchoolStudentTransform将临时表中的结果复制到实际表格中,然后截断临时表格 – Maro 2013-03-19 10:00:33

+0

由您决定何时调用它。你可以从C#或Sql做到,或者只是在某个网页上有一个按钮。 – Romoku 2013-03-19 10:05:31

+0

我喜欢你给的例子,特别是我没有SqlBulkCopy的经验。通常这是一个批处理应用程序,应该在没有用户交互的情况下运行,首先插入所有学生,然后是所有学校,然后是学校。还有什么是dataReader,无需我必须通过数据表对吗?我很抱歉所有这些问题:-) – Maro 2013-03-19 10:10:01