c#推送到数据库真的很慢

时间:2016-03-17 09:43:46

标签: c# sql sql-server-2008 sqlcommand

我正在制作一个文件夹/文本文件阅读器。获取的数据应该推送到SQL服务器。然而,它真的,真的......慢。

真的很慢=>

需要插入的数据:

2.73 GB(2,938,952,122字节)

遍布

78,995个文件,5,908个文件夹

文件夹结构为

文件夹(顶级)

  • 文件夹
    • 文件夹
      1. TEXTFILES
      2. ...
    • 更多文件夹 - > 100也许
  • 文件夹
    • 文件夹
      1. TEXTFILES
      2. ..
      3. ..
  • 总共60个文件夹
    • ...
      1. ...

我一直在阅读它们3天或smth

另外因为文件包含很多重复值,我认为它也很慢

我认为正在发生的原因:

  1. 因为我有一个关系数据库,我需要继续打开一个新连接
  2. 嵌套的foreach

    • 有没有办法增加这种戏剧性的表现?
    • 我应该使用SQLbatchcopy吗?您是否可以将它用于关系表,因为我看到的所有示例都只有一个表被填充,忽略了需要插入的外键(我的得到的是通过SQL db中的自动增量生成)
    • 是否还有其他解决办法可以让这更容易?
  3. 源代码:

      static void leesTxt(string rapport, string TreinNaam)
                        {
                            foreach (string textFilePath in Directory.EnumerateFiles(rapport, "*.txt"))
                            {
    
                                string textname = Path.GetFileName(textFilePath);
                                textname = textname.Substring(0, textname.Length - 4);
                                List<string> variablen = new List<string>();
    
    
                                using (StreamReader r = new StreamReader(textFilePath))
                                {
                                    for (int x = 0; x <= 10; x++)
                                        r.ReadLine();
    
                                    string output;
    
                                    while (true)
                                    {
    
                                        output = r.ReadLine();
                                        if (output == null)
                                            break;
    
                                        if (Regex.IsMatch(output, @"^\d"))
                                        {
                                                variablen.Clear();
                                                string[] info = output.Split(' ');
                                                int kolom = 6;
                                                datum = info[0];
    
    
    
    
    
                                            string[] datumTijdelijk = datum.Split(new[] { '/' });
    
                                            try
                                            {
                                                datum = string.Format("{2}/{1}/{0}", 
                                            }
                                            catch
                                            {
                                                datum = "0002/02/02";
                                            }
    
    
                                            try
                                            {
                                                tijd = info[1];
                                            }
                                            catch
                                            {
                                                Debug.WriteLine(tijd);
                                                tijd = "00:00:00.000";
                                            }
                                            try
                                            {
                                                foutcode = info[2];
                                                absentOfPresent = info[4];
                                                teller = info[5];
                                                omschrijving = info[6];
                                            }
                                            catch
                                            {
    
                                            }
    
    
                                            while (kolom < info.Count() - 1)
                                            {
                                                kolom++;
                                                omschrijving = omschrijving + " " + info[kolom];
                                            }
                                            PushFoutenToSQLdb(datum, tijd, foutcode, textname, omschrijving, teller, absentOfPresent, TreinNaam);
                                        }
                                        if (output == string.Empty)
                                        {
                                            output = " ";
                                        }
                                        if (Char.IsLetter(output[0]))
                                        {
                                            if (variablen.Contains(output))
                                                output = output + "*";
    
                                            try
                                            {
                                                PushExtraInfoToSQLdb(output, datum, tijd, foutcode, textname, teller, absentOfPresent, omschrijving, TreinNaam);
                                            }
                                            catch (Exception ex)
                                            {
                                            }
    
                                            variablen.Add(output);
                                        }
    
                                    }
    
    
    
                                   }  
    static void PushExtraInfoToSQLdb(string waarde, string datum, string tijd, string foutcode, string module, string teller, string Mnemo, string omschrijving, string treinNaam)
                    {
                        myCommand = new SqlCommand("INSERT INTO [Events].[dbo].[ExtraInfo] (Value,FoutId) Values (@waarde,(SELECT FoutId from [Events].[dbo].[Fouten] WHERE Datum = @datum AND Time = @tijd AND FoutCode = @foutcode AND TreinId = (SELECT TreinId from [Events].[dbo].[Treinen] WHERE Name = @treinNaam)))", myConnection);
                        myCommand.Parameters.AddWithValue("@waarde", waarde);
                        myCommand.Parameters.AddWithValue("@datum", datum);
                        myCommand.Parameters.AddWithValue("@tijd", tijd);
                        myCommand.Parameters.AddWithValue("@foutcode", foutcode);
                        myCommand.Parameters.AddWithValue("@module", module);
                        myCommand.Parameters.AddWithValue("@teller", teller);
                        myCommand.Parameters.AddWithValue("@Mnemo", Mnemo);
                        myCommand.Parameters.AddWithValue("@omschrijving", omschrijving);
                        myCommand.Parameters.AddWithValue("@treinNaam", treinNaam);
    
    
                        try
                        {
                            myCommand.ExecuteNonQuery();
                        }
                        catch (Exception ex)
                        {
                        }
    
                    } 
    static void PushFoutenToSQLdb(string datum, string tijd, string foutcode, string module, string omschrijving, string teller, string absentPresent, string treinNaam)
                        {
                            myCommand = new SqlCommand("INSERT INTO [Events].[dbo].[Fouten]  (Datum ,FoutCode, Omschrijving, Module,Time,Teller,Mnemo, TreinId)  Values (@datum , @foutcode, @omschrijving, @module, @tijd, @teller, @absentPresent ,(SELECT TreinId from [Events].[dbo].[Treinen] WHERE Name = @treinNaam))", myConnection);
                            myCommand.Parameters.AddWithValue("@datum", datum);
                            myCommand.Parameters.AddWithValue("@tijd", tijd);
                            myCommand.Parameters.AddWithValue("@foutcode", foutcode);
                            myCommand.Parameters.AddWithValue("@module", module);
                            myCommand.Parameters.AddWithValue("@teller", teller);
                            myCommand.Parameters.AddWithValue("@omschrijving", omschrijving);
                            myCommand.Parameters.AddWithValue("@absentPresent", absentPresent);
                        myCommand.Parameters.AddWithValue("@treinNaam", treinNaam);
    
                        try
                        {
                            myCommand.ExecuteNonQuery();
                        }
                        catch (Exception ex)
                        {
    
                        }
                    }
    

1 个答案:

答案 0 :(得分:1)

由于这些都是插入,并且正如每个人都指出的那样,逐行插入并不是一个好方法。查看SqlBulkCopy Class。这是为了直接从代码中将批量/批量插入编写到数据库,并考虑到性能。

摘自文档。

  

Microsoft SQL Server包含一个名为bcp的流行命令提示实用程序,用于将数据从一个表移动到另一个表,无论是在单个服务器上还是在服务器之间。 SqlBulkCopy类允许您编写提供类似功能的托管代码解决方案。 还有其他方法可以将数据加载到SQL Server表中(例如INSERT语句),但SqlBulkCopy比它们具有显着的性能优势。

您最好的方法(因为您的数据来自文本文件)可能是创建内存DataTable,然后您可以在其中定义与数据库模式匹配的模式。然后,使用要插入的数据填充此表,并调用WriteToServer方法并传入表。

由于SqlBulkCopy仅支持每个实例写入1个表,因此您必须执行此操作2次,一次用于Fouten表,一次用于ExtraInfo表。当您在现有SELECT语句中使用INSERT语句时,您还必须提前获取一些信息,然后可以在传递它之前填充DataTable。到SqlBulkCopy实例。 SqlBulkCopy也不能与Transactions一起使用,因此在插入数据之前必须对数据进行整理,因为如果一条或多条记录存在验证错误,则无法轻松地将所有数据回滚。< / p>

伪代码

  1. 在内存中创建密钥= TreinId和值= Name的字典,并使用DataReader从现有数据中填充。
  2. Fouten创建与Fouten表架构
  3. 匹配的DataTable
  4. 使用文本文件中的数据填充表格,并使用步骤1中创建的词典TreinId
  5. 致电SqlBulkCopy,上传您的数据
  6. 在内存中创建key = Date + Time + FoutCode + TreinNaam,value = FoutId,使用DataReader从现有(新)数据中填充此字典。 似乎有点浪费,也许有更好的方法来定义这个查找?
  7. 使用文本文件中的数据填充ExtraInfo表,并使用在步骤5中创建的词典将FK值返回到Fouten
  8. 致电SqlBulkCopy,上传您的数据
  9. 现有结构的其他注意事项

    1. 运行Sql Server Profiler并查看是否有其他因素导致您不知道的慢插入(例如INSERT中未调整的SELECT语句)。如果你看到你定义的单个插入语句非常昂贵,那么执行上述SqlBulkCopy几乎不会产生影响。
    2. 您的评论中指出的表格很大。在您一次执行1个INSERT语句中,您还为每个语句执行SELECT。如果未正确定义索引,则可能正在为每个插入中的每个SELECT执行表扫描。这将导致巨大的性能损失。最好的解决方法:
      1. 通过在字典中缓存内存中可能的值列表,从INSERT中删除select。使用DataReader来完成此任务。
      2. 调整索引以确保它们在SELECT语句的INSERT部分中使用。
    3. 确保INSERT命令中定义的数据与架构中的数据类型完全匹配。示例:如果您有一个NVARCHAR列,但将类型定义为VARCHAR,则可能需要更长时间,或者在数据库中使用BIGINT,但是从代码传递Int32可能需要更长时间,因为这些必须进行转换。
    4. 确保您的表没有索引或统计信息,这些索引或统计信息对于您执行的每个UPDATE操作进行更新都很昂贵。您可以禁用它们,然后在完成后重建,如果这会导致问题。
    5. 检查数据库上的自动增长设置以及为LOG(也是数据文件)保留的空间量。如果你的空间不足,那么默认情况下,默认值为10MB,大型数据库只增加10MB,并且会经常发生大量的INSERTS。在这里,您应该增加数据文件的大小。这也减少了磁盘碎片。有关详细信息,请参阅Considerations for the "autogrow" and "autoshrink" settings in SQL Server
    6. 检查表格上的触发器。如果您有插入触发器,这些可能会导致性能显着下降。