问题 将.csv文件导入Microsoft SQL Server 2008 R2的推荐方法?


您推荐将.csv文件导入Microsoft SQL Server 2008 R2的方法是什么?

我想要一些快速的东西,因为我有一个包含很多.csv文件的目录(500个.csv文件分布在500个以上)。

我在Win 7 x64上使用SQL Server 2008 R2。

更新:解决方案

这是我如何解决问题的结局:

  1. 我放弃了尝试使用LINQ to Entities来完成这项工作。它有效 - 但它不支持批量插入,所以它大约慢20倍。也许LINQ to Entities的下一个版本将支持这一点。
  2. 在这个线程上给出了建议,使用了批量插入。
  3. 我创建了一个使用批量插入的T-SQL存储过程。数据进入临时表,进行规范化,然后复制到目标表中。
  4. 我使用LINQ to Entities框架将存储过程映射到C#(www.learnvisualstudio.net上有一个显示如何执行此操作的视频)。
  5. 我编写了所有代码来循环访问C#中的文件等。
  6. 这种方法消除了最大的瓶颈,即从驱动器读取大量数据并将其插入数据库。

这种方法读取.csv文件的速度非常快? Microsoft SQL Server可以使用自己的高度优化的例程直接从硬盘驱动器将文件直接导入数据库。大多数其他基于C#的解决方案需要更多代码,而有些(如LINQ to Entities)最终必须通过C#-to-SQL-server链接将数据缓慢地传输到数据库中。

是的,我知道拥有100%纯C#代码来完成这项工作会更好,但最终:

  • (a)对于这个特殊问题,使用T-SQL需要 许多 与C#相比,代码更少,约为1/10,特别是对于从登台表中反规范化数据的逻辑。这更简单,更易于维护。
  • (b)使用T-SQL意味着您可以利用本机批量插入过程,从20分钟等待到30秒暂停加速。

11727
2018-02-16 00:50


起源

好的(和你的)!感谢您的提示 - 对我也很有用。只是想知道为什么有100%的C#代码来完成这项工作会很好?对我来说,拥有一个没有任何C#代码的100%T-SQL解决方案是很好的:)我使用xp_dirtree来获取我的CSV文件列表。看到 patrickkeisler.com/2012/11/... - DaveBoltman


答案:


在T-SQL脚本中使用BULK INSERT似乎是一个很好的解决方案。

http://blog.sqlauthority.com/2008/02/06/sql-server-import-csv-file-into-sql-server-using-bulk-insert-load-comma-delimited-file-into-sql-服务器/

您可以使用xp_cmdshell和dir命令(稍微清理)获取目录中的文件列表。在过去,我尝试使用sp_OAMethod和VBScript函数执行类似的操作,并且必须使用dir方法,因为我无法使用FSO对象获取文件列表。

http://www.sqlusa.com/bestpractices2008/list-files-in-directory/


7
2018-02-16 00:54



谢谢,这似乎工作得很好。我已经用最终解决方案的确切方式更新了问题。 - Contango
好的,我打算做同样的事情,这非常有帮助。但是由于存储过程可以并行执行多次,我确实需要对随机登台表进行批量插入(每次都表示一个唯一的表名),一旦处理完成,我将清理登台表。 - Gopalakrishnan SA


如果您必须对除插入文件之外的文件中的数据执行任何操作,那么我建议使用SSIS。它不仅可以插入和/或更新,还可以为您清理数据。


3
2018-02-16 01:34



感谢SSIS的信息,它看起来很有趣。我不得不安装Visual Studio 2008来检查它(它在Visual Studio 2010中不可用)。 - Contango
SSIS是垃圾,不要使用SSIS。为什么?没有单元测试,有时最终会在应用程序和SSIS包之间出现重复的业务逻辑等。如果您的导入过程很简单,只需使用SSIS,您就不需要进行单元测试,也不会使用任何与您应用程序其余部分类似的逻辑。 - Keith


第一种官方支持的导入大型文本文件的方法是使用命令行工具“bcp”(批量复制实用程序),对大量二进制数据非常有用。

请查看此链接: http://msdn.microsoft.com/en-us/library/ms162802.aspx

但是,在SQL Server 2008中,我假设BULK INSERT命令是您的首选,因为它首先成为标准命令集的一部分。如果由于任何原因你必须保持垂直兼容性,我会坚持使用bcp实用程序,也可用于SQL Server 2000。

HTH :)

编辑后来:谷歌搜索我回忆说,SQL Server 2000也有BULK INSERT命令...但是,显然有一些原因我坚持使用bcp.exe,我不记得为什么......或许有一些限制,我想。


2
2018-02-16 01:01



BCP工作正常,但是,我决定使用批量插入,因为我可以使用LINQ to Entity框架轻松地将存储过程映射到C#。谢谢你的推荐! - Contango


我应该推荐这个:

using System;
using System.Data;
using Microsoft.VisualBasic.FileIO;

namespace ReadDataFromCSVFile
  {
    static class Program
      {
        static void Main()
        {
            string csv_file_path=@"C:\Users\Administrator\Desktop\test.csv";
            DataTable csvData = GetDataTabletFromCSVFile(csv_file_path);
            Console.WriteLine("Rows count:" + csvData.Rows.Count);            
            Console.ReadLine();
        }
    private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
        {
            DataTable csvData = new DataTable();
            try
            {
              using(TextFieldParser csvReader = new TextFieldParser(csv_file_path))
                 {
                    csvReader.SetDelimiters(new string[] { "," });
                    csvReader.HasFieldsEnclosedInQuotes = true;
                    string[] colFields = csvReader.ReadFields();
                    foreach (string column in colFields)
                    {
                        DataColumn datecolumn = new DataColumn(column);
                        datecolumn.AllowDBNull = true;
                        csvData.Columns.Add(datecolumn);
                    }
                    while (!csvReader.EndOfData)
                    {
                        string[] fieldData = csvReader.ReadFields();
                        //Making empty value as null
                        for (int i = 0; i < fieldData.Length; i++)
                        {
                            if (fieldData[i] == "")
                            {
                                fieldData[i] = null;
                            }
                        }
                        csvData.Rows.Add(fieldData);
                    }
                }
            }
            catch (Exception ex)
            {
            }
            return csvData;
        }
      }
    }

//Copy the DataTable to SQL Server using SqlBulkCopy

    function static void InsertDataIntoSQLServerUsingSQLBulkCopy(DataTable csvData)
    {
       using(SqlConnection dbConnection = new SqlConnection("Data Source=ProductHost;Initial Catalog=yourDB;Integrated Security=SSPI;"))
            {
              dbConnection.Open();
              using (SqlBulkCopy s = new SqlBulkCopy(dbConnection))
                {
                    s.DestinationTableName = "Your table name";

                    foreach (var column in csvFileData.Columns)
                    s.ColumnMappings.Add(column.ToString(), column.ToString());

                    s.WriteToServer(csvFileData);
                 }
             }
      }

2
2017-11-04 07:48





如果所有CSV的结构都相同,我建议您使用Integration Services(SSIS)在它们之间循环并将它们全部插入到同一个表中。


1
2018-02-16 02:28



感谢关于SSIS的信息,它看起来很有趣,而且功能非常强大。我不得不安装Visual Studio 2008来检查它(它在Visual Studio 2010中不可用)。 - Contango


我明白这不是你的问题。但是,如果您遇到使用直插入的情况,请使用tablock并插入多行。取决于行大小,但我通常会在600-800行。如果它是一个空表的加载,那么有时删除索引并在加载后创建它们会更快。如果可以在聚集索引加载之前对其进行排序。如果可以,请使用IGNORE_CONSTRAINTS和IGNORE_TRIGGERS。如果可以,将数据库置于单用户模式。

使用AdventureWorks2008R2; 走 使用(tablock)插入Production.UnitMeasure VALUES(N'FT2',N'Square Feet','20080923'),(N'Y',N'Yards','20080923'),(N'Y3',N'Cubic Yards','20080923') ; 走


1
2017-07-18 23:09



尼斯。我建议使用此方法插入#temp表,然后将#temp与主表合并。由于某种原因,它的速度要快得多。 - Contango
可能主表是活动的,因此很难获得holdlock。 #temp表的好处是你有独家访问权限。表到表似乎比外部连接具有更高的优先级。我可以从经验告诉你,按聚集索引排序的加载是主要的。如果我无法对源进行排序,我会将其加载到#temp中,这样我就可以加载到按簇索引排序的真实表中。但我们不应该使用stackoverflow作为讨论线程。 - paparazzo