连接应该多久关闭/打开一次?

l--*_*''' 11 c# sql sql-server

我在C#上逐行写入SQL服务器上的两个表.

我的C#app将参数传递给2个存储过程,每个过程都将行插入表中.

每次调用存储过程时,我都会打开然后关闭连接.

我需要在数据库中写入大约100米的行.

每次调用存储过程时,我应该关闭并打开连接吗?

这是我正在做的一个例子:

public static void Insert_TestResults(TestResults testresults)
        {
            try
            {
                DbConnection cn = GetConnection2();
                cn.Open();

                // stored procedure
                DbCommand cmd = GetStoredProcCommand(cn, "Insert_TestResults");
                DbParameter param;

                param = CreateInParameter("TestName", DbType.String);
                param.Value = testresults.TestName;
                cmd.Parameters.Add(param);


                if (testresults.Result != -9999999999M)
                {
                    param = CreateInParameter("Result", DbType.Decimal);
                    param.Value = testresults.Result;
                    cmd.Parameters.Add(param);
                }


                param = CreateInParameter("NonNumericResult", DbType.String);
                param.Value = testresults.NonNumericResult;
                cmd.Parameters.Add(param);

                param = CreateInParameter("QuickLabDumpID", DbType.Int32);
                param.Value = testresults.QuickLabDumpID;
                cmd.Parameters.Add(param);
                // execute
                cmd.ExecuteNonQuery();

                if (cn.State == ConnectionState.Open)
                    cn.Close();

            }
            catch (Exception e)
            {

                throw e;
            }

        }
Run Code Online (Sandbox Code Playgroud)

这是服务器上的存储过程:

USE [SalesDWH]
GO
/****** Object:  StoredProcedure [dbo].[Insert_TestResults]    Script Date: 12/26/2011 10:45:08 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
ALTER PROCEDURE [dbo].[Insert_TestResults]
    -- Add the parameters for the stored procedure here

    @TestName varchar (500),
    @Result decimal (18,4)=null,
    @NonNumericResult varchar (50)=null, 
    @QuickLabDumpid int

AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

INSERT INTO [SalesDWH].[dbo].[TestResults]
           ([TestName]
           ,[Result]
           ,nonnumericresult
           ,[QuickLabDumpid])
     VALUES
           (@TestName,@Result,@nonnumericresult,@QuickLabDumpID)


END
Run Code Online (Sandbox Code Playgroud)

大约100米行需要3天.这对我来说似乎太慢了.我该怎么做才能加快速度呢?关于打开/关闭连接的标准有多少次?

kuu*_*nbo 17

还有一个选择..NET Framework 自2.0以来就有了SqlBulkCopy类.您必须要做的主要是确保DataTable架构与您的表匹配.在您的测试用例中,类似这样的事情:

private void _initDataTable() {
  dt = new DataTable();
  dt.Columns.Add(new DataColumn()  {
    DataType = Type.GetType("System.String"), 
    ColumnName = "TestName"
  });
  dt.Columns.Add(new DataColumn()  {
    DataType = Type.GetType("System.Decimal"), 
    ColumnName = "Result"
  });
  dt.Columns.Add(new DataColumn()  {
    DataType = Type.GetType("System.String"), 
    ColumnName = "NonNumericResult"
  });
  dt.Columns.Add(new DataColumn()  {
    DataType = Type.GetType("System.Int32"), 
    ColumnName = "QuickLabDumpid"
  });
}
Run Code Online (Sandbox Code Playgroud)

数据访问代码如下所示:

private void _insertData() {
  using (var c = new SqlConnection(CS)) {
    c.Open();
    using (var trans = c.BeginTransaction()) {
      try {
        using (var bc = new SqlBulkCopy(
          c, SqlBulkCopyOptions.TableLock, trans))
        {
          bc.DestinationTableName = "dbo.Insert_TestResults";
          bc.WriteToServer(dt);
        }
        trans.Commit();
      }
      catch (Exception e) {
        trans.Rollback();
        throw;
      }
    }
  }
}  
Run Code Online (Sandbox Code Playgroud)

测试了这样的1000万条记录:

private void _fillDataTable() {
  int batchToInsert = 1000000;
  int numberOfTimes = 10;
  int recordCounter = 1;
  for (int i = 0; i < numberOfTimes; ++i) {
    for (int j = 0; j < batchToInsert; j++) {
      var row = dt.NewRow();
      row[0] = string.Format("TestName{0}", recordCounter);
      row[1] = (decimal) i;
      row[2] = string.Format("NonNumericResult{0}", recordCounter);
      row[3] = i;
      dt.Rows.Add(row);
      recordCounter += 1;
    }
    _insertData();
    dt.Clear();
  }
}
Run Code Online (Sandbox Code Playgroud)

我的开发机器花了两分半钟的时间.您可能想要尝试一次批量处理多少条记录.(不像上面的测试案例那样100万)显然你把这个数据量的10倍以上放入表中(猜测你的实时数据会更大),但我非常怀疑这个方法会不会3天 :)

无论你决定采用什么方法,祝你好运.

编辑:如果不明显,我忘了提 - 因为你在设置DestinationTableName属性时指定了表名,这就是你需要的 - 没有存储过程或任何其他SQL语句.


I a*_*ica 13

如果您使用的是SQL Server 2008,则可以通过表值参数一次发送多条记录:

create type testResultUpload as table
(
    TestName varchar(500),
    Result decimal(18,4) null,
    NonNumericResult varchar(50) null, 
    QuickLabDumpid int
)
Run Code Online (Sandbox Code Playgroud)

然后你可以在客户端建立一个DataTable并将其作为一个块传递给sql.虽然,你可能想要一次做一千个开始.

从参数定义开始,您必须修改存储过程以处理输入记录集

alter proc Insert_TestResult
(
    @testResultUpload testResultUpload readonly -- tvp must be readonly
)
as begin       

    -- This is short and sweet for demonstrative purposes
    -- but you should explicitly list your columns
    insert [SalesDWH].[dbo].[TestResults] 
    select
     *
    from @testResultImport

end
Run Code Online (Sandbox Code Playgroud)

然后在您的客户端:

// create your datatable in the form of the newly created sql type
var dt = new DataTable();
dt.Columns.Add("TestName", typeof(String));
dt.Columns.Add("Result", typeof(Decimal));
dt.Columns.Add("NonNumericResult", typeof(String));
dt.Columns.Add("QuickLabDumpid", typeof(String));

// add your rows here (maybe do it in steps of a thousand
// 100 Million over the pipe at once is ill-advised)
// call the following code to hit sql

using (var cnx = new SqlConnection("your connection string"))
using (var cmd = new SqlCommand {
    Connection = cnx,
    CommandType = CommandType.StoredProcedure,
    CommandText = "dbo.Insert_TestResults",
    Parameters = {
        new SqlParameter {
            ParameterName = "@testResultUpload",
            Value = dt,
            SqlDbType = SqlDbType.Structured // make sure to specify structured
        }
    }
})
{
    cnx.Open();
    cmd.ExecuteNonQuery();
}
Run Code Online (Sandbox Code Playgroud)


Ric*_*kNZ 6

您无需为每个请求打开连接.您可以在开始时打开它一次,并在完成后关闭它.但是,启用连接池(默认情况下),打开和关闭连接并不是一个昂贵的过程.

你的程序很慢,主要是因为:

  1. 每个插入的行都在一个单独的单个事务中
  2. 每行单独的DB往返

第一个修复是将插入分组到事务中 - 每个事务可能有1000行或类似的事情.

第二种方法的修复方法是使用命令批处理(一次发送多个命令,用分号分隔)或表值参数.TVP也很好,因为INSERT INTO SELECT FROM命令作为单个事务执行.

可实现的插入速度也受到日志磁盘速度的限制.确保DB日志位于与DB数据分开的磁盘上.确保日志碎片化并预先生长到您需要的大小也会有所帮助.

使用SqlBulkCopy是另一种选择,它还可以帮助最小化数据库日志的负载,具体取决于它的配置方式.

此外,如果您同时插入100M行,则可以考虑在启动之前删除表中的任何索引,并在完成后重新添加它们.否则,如果您不按聚簇索引的顺序插入行,它将非常快速地分段,对于非聚簇索引,您基本上是插入到主表中的每个插入的附加表中 - 在顶部碎片问题.