将大量数据行传递给存储过程

时间:2010-12-30 11:57:37

标签: c# xml

我正在通过C#阅读电子表格文件...我的电子表格有超过1000行。我需要将每行数据发送到存储过程以执行一些数据库端逻辑和更新记录。我需要一些帮助才能在一次往返中发送所有这1000行数据以节省时间。将所有这1000行数据分组的技术是什么?

2 个答案:

答案 0 :(得分:21)

猜测您使用的是SQL Server 2008或更高版本,您有几个选择。所有这些选项都在新奥尔良的Tech-Ed 2010和video of the session is available online中详细介绍。以下是所提供选项的摘要。

选项#1 - 批量插入(video未真正涵盖)

如果您只需要将数据“转储”到表中,并且除了将数据放入数据库之外,您不需要对数据执行太多操作,这是一个很好的选择。使用SqlBulkCopy对象在ADO.NET中也支持这一点。我还编写了lightweight wrapper you can find on CodePlex以便更轻松地使用SQL Server和ADO.NET

选项#2 - 传递分隔列表

获取所有数据并构建一个大字符串并将整个字符串传递给存储过程。这是惊人的快,但带来了很多包袱。你必须有一个split函数来获取数据,并获得使用SQL-CLR进行拆分所需的最佳性能,如果你不拥有数据库,那么它可以是showstopper。

选项#3 - 传递为XML

这几乎与选项#2相同,因为您再次将一个巨大的字符串传递给一个参数。这也具有合理的性能,但也带有与Option#2相同但没有拆分功能的大部分行李,因为Sql Server知道如何解析XML。

选项#4 - 使用表值函数(SQL Server 2008)

此选项非常酷,可提供最佳性能。首先在SQL Server中创建一个类型为“Table”的值类型,然后创建一个存储过程,将该值类型作为参数。在C#中,您现在可以创建一个SqlCommand并添加一个类型为SqlDbType.Structured的参数。

cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Test.spTVP";
var p = cmd.Parameters.Add("@Values", SqlDbType.Structured);
p.TypeName = "Test.OrderTableType";
p.Value = dataTable;
cmd.Execute…;

执行存储过程时,存储过程的表变量中提供了所有数据。它可以像任何其他表变量一样使用,因此移动数据非常简单。

选项#5 - 使用 Streaming 表值函数(SQL Server 2008)

还有一些工作,然后选项#4,因为你必须设置一个迭代器但是你得到了一些疯狂的性能,因为你不必在将它传递到存储过程之前加载客户端上的所有数据。 .NET Runtime实际上将数据流式传输到数据库中,并且存储过程的实现是相同的。

class MyStreamingTvp : IEnumerable<SqlDataRecord> { …
}
…
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Test.spTVP";
var p = cmd.Parameters.Add("@Values", SqlDbType.Structured);
p.TypeName = "Test.OrderTableType";
p.Value = new MyStreamingTvp(…);
cmd.Execute…;

我在开头提到的video中详细介绍了所有这些选项并略显幽默。这是我今年在Tech-Ed最喜欢的课程之一。

答案 1 :(得分:4)

Ryan的答案非常透彻,涵盖了各种选项。对于相对较少的行(1000-5000是很小的考虑因素),我将使用概述为选项#3,将XML作为存储过程参数传递。我们经常在我的商店里这样做,以下是与之相关的代码:

我假设您的电子表格数据很简单,并且您已经在代码中随时可以使用它,就像您创建的List或DataTable一样。对于这个简单的例子,为简单起见,我假设你的数据是一个DataTable。

我和Ryan一样,也在假设SQL 2008。

1 - 通过将数据转换为将传递给存储过程的XML来准备C#中的数据。这只是一串XML。我们在Base Data类中使用了一个方法。传入DataTable,它会将其转换为一个简单的XML字符串,您可以将其作为参数传递给存储过程。

    public string ConvertToXMLDataString(DataTable table) {
        StringBuilder XMLString = new StringBuilder();
        if (string.IsNullOrEmpty(table.TableName))
            table.TableName = "DataTable";
        XMLString.AppendFormat("<{0}>", table.TableName);
        DataColumnCollection tableColumns = table.Columns;
        foreach (DataRow row in table.Rows) {
            XMLString.AppendFormat("<RowData>");
            foreach (DataColumn column in tableColumns) {
                XMLString.AppendFormat("<{1}>{0}</{1}>", row[column].ToString(), column.ColumnName);
            }
            XMLString.AppendFormat("</RowData>");
        }
        XMLString.AppendFormat("</{0}>", table.TableName);
        return XMLString.ToString();
    }

2 - 我创建了一个简单的示例DataTable,它将包含1000行数据,所有整数,10列

    DataTable table = new DataTable("DataTable");
            for(int i = 1; i < 11; i++){
                table.Columns.Add(new DataColumn("Column" + i.ToString()));
            }
            int j = 0;
            for (int i = 0; i < 1000; i++) {
                DataRow newRow = table.NewRow();
                for (int k = 0; k < table.Columns.Count; k++) {
                    newRow[k] = j++;
                }               
                table.Rows.Add(newRow);
            }

将DataTable传递给ConvertToXMLDataString的最终结果是DataTable的格式良好的XML表示,可以将其传递到存储过程并可以从以下位置轻松选择:

<DataTable>
    <RowData>
        <Column1>0</Column1>
        <Column2>1</Column2>
        <Column3>2</Column3>
        <Column4>3</Column4>
        <Column5>4</Column5>
        <Column6>5</Column6>
        <Column7>6</Column7>
        <Column8>7</Column8>
        <Column9>8</Column9>
        <Column10>9</Column10>
    </RowData>
    <RowData>
        <Column1>10</Column1>
        <Column2>11</Column2>
        <Column3>12</Column3>
        <Column4>13</Column4>
        <Column5>14</Column5>
        <Column6>15</Column6>
        <Column7>16</Column7>
        <Column8>17</Column8>
        <Column9>18</Column9>
        <Column10>19</Column10>
    </RowData>
</DataTable>

3 - 现在,创建一个存储过程来处理已经传递给它的XML数据字符串。

    CREATE PROCEDURE [dbo].[pr_Test_ConvertTable] 
@TableData XML
AS
BEGIN
    SET NOCOUNT ON
    SET ANSI_NULLS ON
    SET ARITHABORT ON

    DECLARE @TempTable TABLE (
        Column1 int, Column2 int, Column3 int, Column4 int, Column5 int,
        Column6 int, Column7 int, Column8 int, Column9 int, Column10 int
    )

    INSERT INTO @TempTable (Column1, Column2, Column3, Column4, Column5, Column6, Column7, Column8, Column9, Column10)
    SELECT XmlTable.Data.value('(./Column1)[1]','int'), XmlTable.Data.value('(./Column2)[1]','int'), 
    XmlTable.Data.value('(./Column3)[1]','int'), XmlTable.Data.value('(./Column4)[1]','int'),
    XmlTable.Data.value('(./Column5)[1]','int'), XmlTable.Data.value('(./Column6)[1]','int'),
    XmlTable.Data.value('(./Column7)[1]','int'), XmlTable.Data.value('(./Column8)[1]','int'),
    XmlTable.Data.value('(./Column9)[1]','int'), XmlTable.Data.value('(./Column10)[1]','int')
    FROM @TableData.nodes('//DataTable/RowData') AS XmlTable(Data)

    SELECT * FROM @TempTable
END
GO

4 - 该过程接受@TableData的XML变量,并将其插入到一个名为@TempTable的新创建的表变量中。

最后一步是使用正确的XML参数创建数据库调用。像往常一样调用SP,只需将其用作参数。

cmd.Parameters.Add("@TableData", SqlDbType.Xml).Value = ConvertToXMLDataString(table);

你有它。您应该能够相应地进行调整以处理您的数据。我通常讨厌传递DataTables,更倾向于传递一个Object或一个List,但在这种情况下,你可能已经将数据放在了DataTable中。

如果这是一次又一次,或者不常见的事情,那么你所采取的性能就会受到影响,因为使用XML的便利性很小。如果这是许多用户经常发生的事情,请使用更有效的方法。