【问题标题】:Uploading CSV to SQL Server将 CSV 上传到 SQL Server
【发布时间】:2011-11-09 15:29:07
【问题描述】:

我正在构建一个每月读取 5 个 CSV 文件的系统。这些文件应该遵循一定的格式和顺序。我有一个主表和 5 个临时表。首先读取每个 CSV 文件,然后将其批量插入到其相应的临时表中。在将 5 个 csv 文件批量插入到它们各自的临时表中后,我再次将临时表中的所有记录插入到主表中。这样可以确保在将数据插入主表之前先上传所有文件。

我使用 ASP.net 构建了这个系统,在调试和测试期间一切正常。每当我将应用程序部署到生产服务器时,就会出现问题。部署应用程序后,我使用了在开发和测试期间上传的相同 csv 文件,系统显示从字符串到日期时间格式的数据转换错误。

我尝试了很多方法来解决此问题,但问题似乎仍然存在。我尝试将生产数据库的排序规则更改为我在开发过程中使用的排序规则。我也尝试在生产服务器中更改一些区域设置,但仍然无法正常工作。

我想也许我可以以编程方式处理这个问题,而不是从临时表批量插入到主表中,我会编写某种 for 循环,将每条记录手动插入到主表中,但我想它会造成性能问题,因为我每次将插入大约 100,000 条记录。

我想知道是否有人在部署过程中遇到过类似的问题。部署后应用程序的行为发生了变化,这对我来说仍然很奇怪。

以下是代码的一部分,它将 inventory.csv 文件上传到服务器,然后将 csv 批量插入临时表 TB_TEMP_INVENTORY,然后将记录从 temp 插入主表 TB_CATTLE。这是对其他 4 个文件完成的,几乎与此相同。

        OleDbConnection conn = new     OleDbConnection(ConfigurationManager.AppSettings["LivestockConnectionString"]);
        OleDbCommand comm;
        OleDbDataAdapter adapter;
        DataTable table = new DataTable();
        string file = string.Empty;
        string content = string.Empty;
        StreamReader reader;
        StreamWriter writer;
        string month = monthDropDownList.SelectedValue;
        string year = yearDropDownList.SelectedItem.Text;

        // upload inventory file
        file = System.IO.Path.GetFileName(inventoryFileUpload.PostedFile.FileName);
        inventoryFileUpload.PostedFile.SaveAs("C://LivestockCSV//" + file);

        // clean inventory file
        file = "C://LivestockCSV//" + file;
        reader = new StreamReader(file);
        content = reader.ReadToEnd();
        reader.Close();
        writer = new StreamWriter(file);
        writer.Write(content.Replace("\"", ""));        // remove quotation
        writer.Close();
        writer = new StreamWriter(file);
        writer.Write(content.Replace(",NULL,", ",,"));  // remove NULL
        writer.Close();
        writer = new StreamWriter(file);
        writer.Write(content.Replace(",0,", ",,"));     // remove 0 dates
        writer.Close();
        writer = new StreamWriter(file);
        writer.Write(content.Replace(",0", ","));       // remove 0 dates at eol
        writer.Close();

        try
        {
            conn.Open();
            comm = new OleDbCommand("TRUNCATE TABLE TB_TEMP_INVENTORY", conn);    // clear temp table
            comm.ExecuteNonQuery();

            // bulk insert from csv to temp table
            comm = new OleDbCommand(@"SET DATEFORMAT DMY;
                                    BULK INSERT TB_TEMP_INVENTORY
                                    FROM '" + file + "'" +
                                    @" WITH
                                    (
                                        FIELDTERMINATOR = ',',
                                        ROWTERMINATOR = '\n'
                                    )", conn);
            comm.ExecuteNonQuery();

            // check if data for same month exists in cattle table
            comm = new OleDbCommand(@"SELECT *
                                    FROM TB_CATTLE
                                    WHERE Report='Inventory' AND Month=" + month + " AND Year=" + year, conn);

            if (comm.ExecuteScalar() != null)
            {
                comm = new OleDbCommand(@"DELETE
                                        FROM TB_CATTLE
                                        WHERE Report='Inventory' AND Month=" + month + " AND Year=" + year, conn);
                comm.ExecuteNonQuery();
            }


            // insert into master cattle table
            comm = new OleDbCommand(@"SET DATEFORMAT MDY;
                                    INSERT INTO TB_CATTLE(ID, Sex, BirthDate, FirstCalveDate, CurrentUnit, OriginalCost, AccumulatedDepreciation, WrittenDownValue, NetRealizableValue, CapitalGainLoss, Month, Year, Report, Locked, UploadedBy, UploadedAt)
                                    SELECT DISTINCT ID, Sex, BirthDate, FirstCalveDate, CurrentUnit, 0, 0, 0, 0, 0, " + month + ", " + year + @", 'Inventory', 0, 'Admin', '" + DateTime.Now + @"'
                                    FROM TB_TEMP_INVENTORY", conn);
            comm.ExecuteNonQuery();
            conn.Close();
        }
        catch (Exception ex)
        {
            ClientScript.RegisterStartupScript(typeof(string), "key", "<script>alert('" + ex.Message + "');</script>");
            return;
        }

【问题讨论】:

  • 您的环境发生了变化,因此您的应用程序的行为也发生了变化。什么是 SQL 错误 - 尝试提供一些示例数据,即在开发/测试中有效但在生产中失败的时间字段示例。
  • 出色的帖子@Siva。 SSIS 绝对是我想学习的工具。我浏览了帖子并创建了一个小型测试项目,插入进行得很顺利。有没有办法在我已经完成的应用程序中包含 SSIS 的功能。有没有办法可以实例化或调用 ssis 对象来触发将 csvs 读取到我的数据库中。谢谢你指点我这个
  • @moeabdol:我觉得最好把所有SQL代码封装成一个存储过程,有两个参数CREATE PROCEDURE pImportLiveStock (@pMonth TINYINT, @pYear SMALLINT) AS TRUNCATE TABLE TB_TEMP_INVENTORY;...

标签: c# asp.net sql-server sql-server-2008


【解决方案1】:

您没有指定插入的方式,但这里的一个合理选项是SqlBulkCopy,它可以采用DataTableIDataReader 作为输入;这将为您提供充分的机会按摩数据 - 无论是在内存中 (DataTable),还是通过流 API (IDataReader),同时仍使用高效导入。 CsvReader 是加载 CSV 的不错选择。

另一种选择是使用非常基本的插入到临时表中,并通过 TSQL 代码处理数据。

关于为什么它在开发/生产之间发生了变化;最可能的答案是:

  • 您在 dev 中使用的数据不具有代表性
  • 两者之间存在环境/配置差异

【讨论】:

  • 我在我的应用程序中使用 oledb 提供程序,它不支持批量插入
  • @moeabdol 如果你从 .NET 与 sql server 对话,为什么不使用 SqlClient?
  • oledb 提供程序在后端数据库方面更为通用。如果我们将来随时将数据库从 sql server 更改为 oracle 或 mysql,oledb 将在不更改代码的情况下处理它们。另一方面,如果你使用 sqlclient,那么你只能使用 ms sql server
  • @moeabdol 我想你已经错过了最近的公告——当你认为 oledb 是一个好的、通用的、长期的选择时,你可能会考虑弃用:blogs.msdn.com/b/sqlnativeclient/archive/2011/08/29/… 另见“弃用的 MDAC/WDAC 组件" 这里的部分:msdn.microsoft.com/en-us/library/ms810810.aspx
  • @moeabdol 除非您积极测试这些平台,否则它可能已经不是微不足道的改变 - IMO 这是一个错误的担忧。
【解决方案2】:

1) 检查开发/测试和生产环境的 SQL Server LANGUAGE 和 DATEFORMAT 设置:

DBCC USEROPTIONS

2) CSV 文件中使用什么日期格式(来源)?

3) 日期/时间字段(目的地)使用什么数据类型?

DECLARE @v VARCHAR(10) = '2010-08-23';

SET DATEFORMAT mdy;
SELECT CAST(@v AS DATETIME)
        ,CAST(@v AS DATE)
        ,YEAR(CAST(@v AS DATETIME))
        ,MONTH(CAST(@v AS DATETIME))
        ,DAY(CAST(@v AS DATETIME));

SET DATEFORMAT dmy;
SELECT CAST(@v AS DATETIME)
        ,CAST(@v AS DATE);

【讨论】:

  • 我在两个 sql 服务器上都执行了 dbcc useroptions,它们显示了相同的结果集。 sql server 中的日期格式是 mdy。 csv 文件中的日期格式为 dmy。
  • 服务器本身呢?如果您的 ASP .NET 应用程序正在加载 CSV 本身,那么系统区域设置可能正在生效。除非另有说明,否则应用程序将根据服务帐户的语言环境解析日期。
  • 我假设应用程序在不同的用户帐户的上下文中运行。 (根据内存)如果您使用 Visual Studio 并运行 ASP .NET 应用程序,它将在属于您的用户帐户 (Locale Settings A) 的容器进程中运行,并且很可能已部署的应用程序在 IIS 工作线程中运行IIS 服务帐户 (Local Settings B)。尝试在这两种情况下将当前语言环境转储到临时文件中。
  • (1) 使用 mdy DATEFORMAT 选项(测试环境),从 '16/01/2008' 到 datetime 数据类型的转换将不起作用。 (2) 尝试在 CSV 文件中使用安全的日期格式(对 DATETIME 字段安全):YYYYMMDD(无分隔符)。这是CONVERT function 使用的格式 112。
  • 尝试 (1) 将 ... 'Admin', '" + DateTime.Now + @" ... 替换为 ... 'Admin', GETDATE(), " + @" ... 或 (2) 使用 126 样式(T-SQL CONVERT 函数)格式化 DateTime.NowString.Format(" '{0:yyyy-MM-ddThh:mm:ss}' ", DateTime.Now) 或 (3) 添加 GETDATE()作为 UploadedAt 字段的默认表达式并从 INSERT 中删除 UploadedAt。
猜你喜欢
  • 2011-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-13
  • 1970-01-01
  • 2012-05-05
  • 2021-12-02
  • 1970-01-01
相关资源
最近更新 更多