【问题标题】:Batch insert to SQL Server table from DataTable using ODBC Connection使用 ODBC 连接从 DataTable 批量插入到 SQL Server 表
【发布时间】:2016-12-25 02:40:30
【问题描述】:

我被要求寻找最有效的方法来获取 DataTable 输入并使用 C# 将其写入 SQL Server 表。问题是解决方案必须始终使用 ODBC 连接,这排除了 sqlBulkCopy。该解决方案还必须适用于 SQL Server 2008 R2 之前的所有 SQL Server 版本。

我认为最好的方法是使用以下 SQL 语法一次使用 1000 行的批量插入:

插入到 dbo.Table1(字段 1,字段 2) 选择值 1,值 2 联盟 选择值1,值2

我已经编写了代码来检查与 DataTable 输入对应的表是否已经存在于 SQL Server 上,如果不存在则创建一个。

我还编写了代码来创建 INSERT 语句本身。我正在努力解决的是如何从数据表中的行动态构建 SELECT 语句。如何访问行中的值来构建我的 SELECT 语句?我想我还需要检查每一列的数据类型,以确定这些值是否需要用单引号 (') 括起来。

这是我当前的代码:

        public bool CopyDataTable(DataTable sourceTable, OdbcConnection targetConn, string targetTable)
    {
        OdbcTransaction tran = null;
        string[] selectStatement = new string[sourceTable.Rows.Count];

        // Check if targetTable exists, create it if it doesn't
        if (!TableExists(targetConn, targetTable))
        {
            bool created = CreateTableFromDataTable(targetConn, sourceTable);

            if (!created)
                return false;
        }

        try
        {
            // Prepare insert statement based on sourceTable
            string insertStatement = string.Format("INSERT INTO [dbo].[{0}] (", targetTable);

            foreach (DataColumn dataColumn in sourceTable.Columns)
            {
                insertStatement += dataColumn + ",";
            }

            insertStatement += insertStatement.TrimEnd(',') + ") ";

            // Open connection to target db
            using (targetConn)
            {
                if (targetConn.State != ConnectionState.Open)
                    targetConn.Open();

                tran = targetConn.BeginTransaction();

                for (int i = 0; i < sourceTable.Rows.Count; i++)
                {
                    DataRow row = sourceTable.Rows[i];

                    // Need to iterate through columns in row, getting values and data types and building a SELECT statement

                    selectStatement[i] = "SELECT ";
                }

                insertStatement += string.Join(" UNION ", selectStatement);

                using (OdbcCommand cmd = new OdbcCommand(insertStatement, targetConn, tran))
                {
                    cmd.ExecuteNonQuery();
                }

                tran.Commit();
                return true;
            }
        }       
        catch 
        {
            tran.Rollback();
            return false;
        }
    }

任何建议将不胜感激。此外,如果有比我建议的更简单的方法,那么任何细节都会很棒。

【问题讨论】:

  • 您是否也排除了 ADO.net 表参数?这是一个奇怪的限制,因为将 Table 参数传递给存储过程并在传入的表上使用 MERGE 命令是迄今为止实现这一点的最有效方法。
  • 问题是我们将在我们正在写入的 SQL Server 上拥有非常有限的权限。我们在桌子上有 CRUD,仅此而已。恐怕任何涉及存储过程和/或 TVP 的解决方案都不在范围内。

标签: c# sql-server odbc batching


【解决方案1】:

好的,因为我们不能使用存储过程或批量复制;几年前,当我对各种方法进行建模时,性能的关键决定因素是对服务器的调用次数。因此,发现将一组 MERGE 或 INSERT 语句批处理成一个用分号分隔的调用是最快的方法。我最终批处理了我的 SQL 语句。我认为 SQL 语句的最大大小是 32k,所以我将我的批次切成了那个大小的单位。

(注意 - 使用 StringBuilder 而不是手动连接字符串 - 它对性能有好处)

Psuedo-code
string sqlStatement = "INSERT INTO Tab1 VALUES {0},{1},{2}";
StringBuilder sqlBatch = new StringBuilder();
foreach(DataRow row in myDataTable)
{
    sqlBatch.AppendLine(string.Format(sqlStatement, row["Field1"], row["Field2"], row["Field3"]));
    sqlBatch.Append(";");
}
myOdbcConnection.ExecuteSql(sqlBatch.ToString());

您需要处理批量大小的复杂性,并在字符串替换步骤中格式化正确的字段数据类型,否则这将是最佳性能。

【讨论】:

  • 你可以分享你用于批量构建SQL语句的代码吗?这就是我目前卡住的地方。
【解决方案2】:

PhillipH的标记解决方案是开放的几个错误和SQL注入。

通常您应该使用参数构建DbCommand 并执行它,而不是执行自构建 SQL 语句。

对于 ODBC 和 OLEDB,CommandText 必须为 "INSERT INTO Tab1 VALUES ?,?,?",SqlClient 需要命名参数(“@”)。

参数要加上底柱的尺寸。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-10
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多