【问题标题】:Sql Bulk Copy/Insert in C#C# 中的 Sql 批量复制/插入
【发布时间】:2013-09-21 08:51:48
【问题描述】:

我是 JSON 和 SQLBulkCopy 的新手。我有一个 JSON 格式的 POST 数据,我想使用 C# 在 Microsoft SQL 中批量复制/插入。

JSON 格式:

{
    "URLs": [{
        "url_name": "Google",
        "url_address": "http://www.google.com/"
    },
    {
        "url_name": "Yahoo",
        "url_address": "http://www.yahoo.com/"
    },
    {
        "url_name": "FB",
        "url_address": "http://www.fb.com/"
    },
    {
        "url_name": "MegaSearches",
        "url_address": "http://www.megasearches.com/"
    }]
}

类:

public class UrlData
{
    public List<Url> URLs {get;set;}
}

public class Url
{
    public string url_address {get;set;}
    public string url_name {get;set;}
}

我怎样才能有效地做到这一点?

【问题讨论】:

  • 批量复制适用于大量行,因此您可以绕过删除和插入的细致日志记录。单行不需要这个,这似乎是你想要的。你传入了多少个网址?

标签: c# .net sql sqlbulkcopy


【解决方案1】:

TL;DR如果您的数据已经表示为DataTable,您可以使用SqlBulkCopy 将其插入到服务器上的目标表中:

string csDestination = "put here the a connection string to the database";

using (SqlConnection connection = new SqlConnection(csDestination))
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
    connection.Open()
    bulkCopy.DestinationTableName = "TUrls";
    bulkCopy.WriteToServer(dataTableOfUrls);
}

如果您只想加载“10 到 50 个网址”,则无需使用 SqlBulkCopy - 它的一般用途是消除数千个单独的插入。

所以,插入不带 SqlBulkCopy [不带EntityFramework] 可以一一完成:

string insertQuery = "insert into TUrls(address, name) values(@address, @name)";
foreach (URL url in listOfUrls)
{
    SqlCommand cmd = new SqlCommand(insertQuery);
    cmd.Parameters.AddWithValue("@name", url.url_name);
    cmd.Parameters.AddWithValue("@address", url.urld_address);

    // Remember to take care of connection! I omit this part for clarity
    cmd.ExecuteNonQuery();
}

使用 SqlBulkCopy 插入数据,您需要将数据(例如自定义类对象列表)转换为 DataTable。以下是来自Marc Gravell's answer 的引用,作为此类转换的通用解决方案示例:

这是一个不错的 2013 年更新,使用 FastMember 来自 NuGet:

IEnumerable<SomeType> data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data)) {
    table.Load(reader);
}

是的,这与this 几乎完全相反; 反思就足够了——或者如果你需要更快, HyperDescriptor 在 2.0 中,或者在 3.5 中可能是 Expression。实际上, HyperDescriptor 应该绰绰有余。

例如:

// remove "this" if not on C# 3.0 / .NET 3.5
public static DataTable ToDataTable<T>(this IList<T> data)
{
    PropertyDescriptorCollection props =
        TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    for(int i = 0 ; i < props.Count ; i++)
    {
        PropertyDescriptor prop = props[i];
        table.Columns.Add(prop.Name, prop.PropertyType);
    }
    object[] values = new object[props.Count];
    foreach (T item in data)
    {
        for (int i = 0; i < values.Length; i++)
        {
            values[i] = props[i].GetValue(item);
        }
        table.Rows.Add(values);
    }
    return table;        
}

现在,将您的数据表示为DataTable,您就可以将其写入服务器上的目标表了:

string csDestination = "put here the a connection string to the database";

using (SqlConnection connection = new SqlConnection(csDestination))
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
    connection.Open()
    bulkCopy.DestinationTableName = "TUrls";
    bulkCopy.WriteToServer(dataTableOfUrls);
}

希望对你有帮助。

UPD

  1. 回答@pseudonym27 问题:“您好,我可以使用 BulkCopy 类将数据附加到 SQL 数据库中的现有表吗?”

是的,您可以 - BulkCopy 的工作方式与插入命令一样,它会附加数据。
此外,考虑使用中间表以防操作出错的可能性很高(例如,插入时间长和连接问题),并且您希望尽可能少地忙/锁定目标表。当然,中间表的另一个用例是需要在插入之前进行一些数据转换。

【讨论】:

  • 感谢您的帮助。有没有办法可以直接将 JSON 反序列化为 DataTable,而不是先将 JSON 转换为某个对象 [URL],然后再转换为 DataTable。例如,Object URL 可以是 DataRow,而 URLData [ListOfURLs] 可以是直接来自反序列化 JSON 的 DataTable。
  • @Ali007 我从未在 c# 中使用过 JSON,所以我完全不知道,但简单的谷歌搜索告诉我没有内置的 [in .Net] 方式。所以最流行的方式——使用 JSON.Net (james.newtonking.com/projects/json-net.aspx) 将其反序列化为 IEnumerable,然后使用 Marc Gravell 的扩展方法将其转换为 DataTable
  • @pseudonym27 抱歉,刚刚提到了您的评论。看我答案底部的upd部分
  • 必须打开连接,所以缺少一些东西:using (SqlConnection destinationConnection = new SqlConnection(csDestination)) using (SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection)) { bulkCopy.DestinationTableName = "TUrls"; bulkCopy.WriteToServer(dataTableOfUrls); };否则会得到错误WriteToServer requires an open and available Connection. The connection's current state is closed.]
【解决方案2】:

使用以下代码,您可以将List&lt;YourClassname&gt; 转换为DataTable:-

List<YourClass> objlist = alldata;
string json = Newtonsoft.Json.JsonConvert.SerializeObject(objlist);
DataTable dt = Newtonsoft.Json.JsonConvert.DeserializeObject<DataTable>(json);
SaveDataInTables(dt, "Table_Name_Of_SQL");

在这里,我假设 alldata 包含 list&lt;YourClass&gt; 对象,您也可以这样做 - objlist.Add(objYourClass),然后在 SaveDataInTables 方法中传递 sql_TableName 和数据表。该方法会将SQL_Table中的所有数据插入。

public void SaveDataInTables(DataTable dataTable, string tablename)
{
   if (dataTable.Rows.Count > 0)
   {
       using (SqlConnection con = new SqlConnection("Your_ConnectionString"))
       {
           using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(con))
           {
               sqlBulkCopy.DestinationTableName = tablename;
               con.Open();
               sqlBulkCopy.WriteToServer(dataTable);
               con.Close();
            }
        }
    }
}

希望这些代码对你有帮助!!!

【讨论】:

    【解决方案3】:

    如果你使用的是> sql server 2005,你应该使用Table valued parameters.。你可以有一个例子here

    【讨论】:

      【解决方案4】:

      如果它只有 10-50 个 url,不经常插入,您可以触发插入语句。简单而省事,您可以使用像 dapper 这样简单快捷的东西。

      否则,如果您想要批量复制,则需要先从 JSON 中创建并填充 ADO.NET 数据表 - 最好与目标 sql 表的架构相匹配。这是你的选择。

      【讨论】:

        【解决方案5】:
        using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
        {
           bulkCopy.DestinationTableName = "dbo.LogData";
           try
           {
              // Write from the source to the destination.
              connection.Open();
              bulkCopy.WriteToServer(dataTable1);
              connection.Close();
           }
           catch (Exception ex)
           {
              Console.WriteLine(ex.Message);
           }
        }
        

        【讨论】:

          【解决方案6】:

          我不知道你使用的是什么数据库。但我有使用 postgres 的经验,我认为这在其他关系数据库中是相似的:

          在数据库中,您还可以从 csv 分隔值复制(例如Postgres)。如果您根据 expeted 输入格式格式化您的字符串,这应该是最快的方法。

          【讨论】:

          猜你喜欢
          • 2019-04-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-24
          • 2016-01-31
          • 2013-02-05
          相关资源
          最近更新 更多