将CSV文件导入SQL Server数据库

时间:2016-06-20 07:12:03

标签: c# sql-server csv

.csv个文件有57列,其中一些值为空。例如,,Jane,Doe,,35。假设自动ID密钥的第一个值(逗号之前)是每次导入数据库时​​递增的。问题是未导入空值,从而导致错误。我在这里理解的是SQL数据库无法读取空值,因此它会移动值,如下所示:Jane,Doe,35,从而使Jane成为ID的值。另一个问题是.csv文件'列与表不匹配。例如,表格中包含ID列,而.csv文件则以Name列开头。有没有办法从特定列开始将其导入数据库?

注意:这只在一个表中。 问题:如果我创建一个与.csv文件列匹配的单独表,然后将其与具有列ID的表(在示例中)连接,是否更为可取?

我的代码到目前为止:

        DataTable dt = new DataTable();

        dt.Columns.AddRange(new DataColumn[54] { new DataColumn("Delay_Code"), and so on... });

        string csvData = File.ReadAllText(e.FullPath);
        foreach (string row in csvData.Split('\n'))
        {
            if (!string.IsNullOrEmpty(row))
            {
                dt.Rows.Add();
                int i = 0;
                foreach (string cell in row.Split(','))
                {
                    dt.Rows[dt.Rows.Count - 1][i] = cell;
                    i++;
                }
            }
        }

        string consString = @"Data Source="blahblah";
        using (SqlConnection con = new SqlConnection(consString))
        {
            using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(con))
            {
                //Set the database table name
                sqlBulkCopy.DestinationTableName = "owner.Table";
                con.Open();
                sqlBulkCopy.WriteToServer(dt);
                con.Close();
            }
        }

对于上面的代码,我创建了另一个与.csv文件的列匹配的表。最初,该表在.csv文件中的列之前有三个前面的列。

最好的方法是什么?

1 个答案:

答案 0 :(得分:1)

从某些角度来看,你的问题可能看起来过于宽泛,但我理解它并且我分享了一些对我有用的广泛(通用)方法,你可以将它作为灵感来使用:

  1. 编写一个高质量的CSV阅读器,它也可以像

    一样处理CSV行
    Value1, Value2, "Value 3", "Value ""4""", "Value
    5",, Value 7, "Value,8"
    
    • 所以如果有双引号字符,它可以接受任何字符,包括空格或换行符,其他引号(写成""),这只停留在另一个单引号(")< / LI>
    • 请准备好分隔符可以是,;或制表符,十进制数字可以包含逗号.,等等。请记住它通常是另一方谁生成CSV,你可能需要适应其格式
    • 最简单的方法就是利用有限状态机,它重量轻,照明速度快,像50 MB /秒。
  2. [可选]逻辑步骤是具有可配置的导入定义,而不是硬连线定义,例如。

    { ExternalOrders = "Data From External Orders in CSV",
      CsvFormat = { CsvHasHeaderRow = true,
                 CsvFieldSeparator = ",",
                 DecimalSeparator = ".",
                 DateFormat = "yyyy-MM-dd",
                 DateTimeFormat = "yyyy-MM-dd hh:mm:ss",
                 TimeFormat = "hh:mm:ss" },
      ColumnMap = {
           Column1 = { SourceColumnName = "OrderID",
                       SourceColumnType = "nvarchar(50)"
                       StagingColumn = 1 },
           Column2 = { SourceColumnName = "OrderDate",
                       SourceColumnType = "date"
                       StagingColumn = 2 },
           ColumnAmount = { SourceColumnPosition = 5,
                       SourceColumnType = "decimal(18,6)"
                       StagingColumn = 3 }
           },
    StagingImportSql = "INSERT INTO Orders (Number, OrdDate, Amount)
                            SELECT CAST(c1 AS navarchar(50),
                                    CAST(c2 AS date),
                                    CAST(c3 AS money)
                                FROM StagingTable
                                WHERE ImportID = {{ImportIDToken}};"
    }
    
    • 这可以在数据库中,在文本配置文件中,无论在哪里。我在3个数据库表(ImportAction,CsvFormat,ColumnMapping)
    • 中有它
  3. 在SQL数据库中使用列

    创建一个临时表
    ID int identity(1,1)
    ImportID int
    CsvRowNumber int
    c1 sqlvariant
    c2 sqlvariant
    c3 sqlvariant
    ...
    c64 sqlvariant
    
  4. 根据配置的CsvFormat创建调用CSV导入(格式为ColumnMap)的引擎以填充登台表。您的临时表中还有其他列,例如导入期间由引擎自动填充ImportIDCsvRecordNumber。为每个CSV发出SQL INSERT语句,以便将一条记录添加到临时表中。完成所有操作后,在您的SQL命令中将令牌{{ImportIDToken}}替换为您实际导入的编号后,启动StagingImportSql

  5. 您可以选择实施

    • 如果您在导入后没有立即清除临时表,则清除临时表,例如

      • 保留一定数量的进口和/或
      • 保持导入时间不超过指定天数
    • 用户限制 - 谁可以查看和运行哪些导入

  6. 我没有分享特定代码,在我的情况下,它位于,并且在许多类中可能有6000行。我只是在分享想法。

    关于解析的注意事项:

    • 让您的CSV阅读器返回DataTable字符串
      • 返回整个表不适合大型CSV,但最多(让我们说)500 MB应该没问题
    • 使用Integer.Parse()Decimal.Parse()Double.Parse()等方法根据列数据类型解析简单值。在解析之前,您可能希望替换标准的自定义小数点分隔符或千位分隔符。
    • 了解日期和时间,请使用DateTime.ParseExact()
    • 对于数据类型的内部处理,枚举System.Data.SqlDbType是您的朋友