使用嵌套关系导入批量Excel数据

时间:2017-07-07 02:53:50

标签: c# entity-framework bulkinsert excel-import

我在使用嵌套关系的Excel数据导入中遇到性能问题。

我有两个要插入的主表和其他四个与主表有一对多和多对多关系的表。

在为两个主表插入数据之前,我检查了四个表的现有或新的导入数据,因为导入的文件可能具有四个表的相同数据,并且相同的数据不能多次插入。

这就是性能缓慢的原因。

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:0)

我不得不用包含数百万条记录的批量数据处理这种情况。从许多浪费的经验来看:

1)尽量不要使用excel。它很慢并且占用了很多内存。具有500,00条记录的单页最终可能会占用超过2千兆字节的内存,只是为了加载文件。单张纸的进口需要30-40-50分钟或更长时间。考虑将数据转换为CSV并使用for (var i = 0; i <= 13; i++) { headingcell[i].BackgroundColor = BaseColor.DARK_GRAY; headingcell[i].BorderWidth = 0; } 导入数据。它可以处理大量的记录,大约几秒钟到几分钟而不是几小时。

2)在这种情况下,当改善实体框架的性能时,你无能为力。我发现最好和最快的方法是将每个工作表加载到数据库中自己的临时表中。然后我构造了SQL来批量插入到他们的结束表中。可以将中间插入的结果捕获到输出表中,以便您可以访问从临时表执行任何连接所需的键或插入到相关表中。您当然可以“窃取”一些自动生成的EF SQL,但是您需要对其进行微调。

3)尽管SQL讨厌循环,但我编写了我的sql语句以循环运行并一次插入100,000条记录。它使插入件运行得更快。

在批量导入每张表单CSV后,为您提供一个想法:

首先根据需要定义存储在相关表中的变量和类型:

SqlBulkCopy

运行插入循环:

DECLARE @Max INT = @RecordsPerLoop
DECLARE @Min INT = 0
DECLARE @TotalRECORD INT = (
        SELECT count(*)
        FROM TempClassMemberRecords
        )
DECLARE @Country VARCHAR(50)

SET @Country = 'USA'

-- Const variables for class member inserts
DECLARE @DefaultCommPreference VARCHAR(50) = (
        SELECT TOP 1 CommPreference
        FROM Actors
        WHERE PKID = 0
        )
    ,@PrimaryActorTypeId INT = (
        SELECT TOP 1 PKId
        FROM ActorTypes
        WHERE ActorTypeName = 'PrimaryClaimant'
        )
    ,@SecondaryActorTypeId INT = (
        SELECT TOP 1 PKId
        FROM ActorTypes
        WHERE ActorTypeName = 'CoClaimant'
        )
    ,@HomePhoneTypeId INT = (
        SELECT TOP 1 PKId
        FROM PhoneTypes
        WHERE PhoneTypeName = 'Home'
        )
    ,@WorkPhoneTypeId INT = (
        SELECT TOP 1 PKId
        FROM PhoneTypes
        WHERE PhoneTypeName = 'Work'
        )
    ,@PrimaryCountryId INT = IsNull((
            SELECT TOP 1 PKId
            FROM Countries
            WHERE @Country IN (
                    CountryName
                    ,CountryCode
                    )
            ), 0)
    ,@DefaultCountryId INT = IsNull((
            SELECT TOP 1 PKId
            FROM Countries
            WHERE CountryCode = 'USA'
            ), 0)
    ,@SubmitTypeId INT = (
        SELECT TOP 1 PKId
        FROM ClaimSubmitTypes
        WHERE SubmitTypeName = 'Bulk'
        )
    ,@ClaimStatusId INT = (
        SELECT TOP 1 PKId
        FROM ClaimStatusTypes
        WHERE StatusName = 'Active'
        )
    ,@ModifiedBy VARCHAR(20) = @uploadUser
    ,@ModifiedDate DATETIME = GETDATE()
    ,@CaseCode VARCHAR(50) = (
        SELECT TOP 1 CaseCode
        FROM Cases
        ORDER BY PKId DESC
        ) + ''
    ,@IndividualClaimantType INT = (
        SELECT TOP 1 PKId
        FROM claimanttypes
        WHERE ClaimantTypeName = 'Individual'
        )
    ,@CompanyClaimantType INT = (
        SELECT TOP 1 PKId
        FROM claimanttypes
        WHERE ClaimantTypeName = 'Corporation'
        )
    ,@Checked BIT = 0
    ,@startingPKId INT = (
        SELECT max(PKId) + 1
        FROM dbo.Entities WITH (NOLOCK)
        );

--Record per group insert
IF (@TotalRECORD <= @RecordsPerLoop)
    SET @max = @TotalRECORD

答案 1 :(得分:0)

使用.Net ReadAllLines()方法将整个文件读入字符串数组对象,然后运行Parallel For循环以并行处理所有行。

private bool ProcessFile(string FolderPath, string FileExtension)
{
    try
    {
        //all files with requisite file extension
        DirectoryInfo dinfo = new DirectoryInfo(FolderPath);
        FileInfo[] Files = dinfo.GetFiles(FileExtension);
        foreach (FileInfo file in Files)
        {
            List<String> AllLines = new List<String>();
            using (StreamReader sr = File.OpenText(file.FullName))
            {
                int x = 0;
                while (!sr.EndOfStream)
                {
                    AllLines.Add(sr.ReadLine());
                    x += 1;
                }
                sr.Close();
            } 

            Parallel.For(0, AllLines.Count, x =>
            { 
                InsertDataCheck(AllLines[x]);
            }); 

        }
        GC.Collect();
        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    return false;
}

private void InsertDataCheck(string Line)
{
   //check if you want to insert data on the basis of your condition
   //and then insert your data    
}